diff options
author | unknown <knielsen@knielsen-hq.org> | 2012-10-12 11:00:01 +0200 |
---|---|---|
committer | unknown <knielsen@knielsen-hq.org> | 2012-10-12 11:00:01 +0200 |
commit | 52c84d144d3b07966d9b3bab8694eb012eef69ce (patch) | |
tree | 2419ab191bc656e3355fdd38b8cf274f68f4a049 | |
parent | d7e0499407aa684f1d09cf295dba9f912b74fd3b (diff) | |
download | mariadb-git-52c84d144d3b07966d9b3bab8694eb012eef69ce.tar.gz |
MDEV-3802: Millisecond timeout support in non-blocking client library + fix incorrect blocking.
After the merge of VIO stuff from MySQL 5.6, there were some bugs left
in the non-blocking client library:
- vio_io_wait() was introduced without any support for non-blocking operation,
so async queries could turn into sync.
- Timeouts were changed to milliseconds, but this was not reflected in the
non-blocking API, also semantics was changed so signed -1 was used for
"no timeout" rather than unsigned 0.
Fix by implementing and using my_io_wait_async() in the non-blocking case. And
by introducing a new mysql_get_timeout_value_ms() API function that provides
the timeout with millisecond granularity. The old mysql_get_timeout_value()
is kept and fixed to work correctly, converting the timeout to whole seconds.
-rw-r--r-- | include/my_context.h | 4 | ||||
-rw-r--r-- | include/mysql.h | 1 | ||||
-rw-r--r-- | include/mysql.h.pp | 1 | ||||
-rw-r--r-- | include/mysql_async.h | 10 | ||||
-rw-r--r-- | sql-common/client.c | 3 | ||||
-rw-r--r-- | sql-common/mysql_async.c | 60 | ||||
-rw-r--r-- | vio/viosocket.c | 30 |
7 files changed, 90 insertions, 19 deletions
diff --git a/include/my_context.h b/include/my_context.h index 1e1b7e6a749..0abf49efd43 100644 --- a/include/my_context.h +++ b/include/my_context.h @@ -182,8 +182,8 @@ struct mysql_async_context { my_bool r_my_bool; } ret_result; /* - The timeout value, for suspended calls that need to wake up on a timeout - (eg. mysql_real_connect_start(). + The timeout value (in millisecods), for suspended calls that need to wake + up on a timeout (eg. mysql_real_connect_start(). */ unsigned int timeout_value; /* diff --git a/include/mysql.h b/include/mysql.h index 6bbcdd96482..046d093dc47 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -852,6 +852,7 @@ int STDCALL mysql_close_start(MYSQL *sock); int STDCALL mysql_close_cont(MYSQL *sock, int status); my_socket STDCALL mysql_get_socket(const MYSQL *mysql); unsigned int STDCALL mysql_get_timeout_value(const MYSQL *mysql); +unsigned int STDCALL mysql_get_timeout_value_ms(const MYSQL *mysql); /* status return codes */ #define MYSQL_NO_DATA 100 diff --git a/include/mysql.h.pp b/include/mysql.h.pp index b177c36f90c..361f1a323ae 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -733,3 +733,4 @@ int mysql_close_start(MYSQL *sock); int mysql_close_cont(MYSQL *sock, int status); my_socket mysql_get_socket(const MYSQL *mysql); unsigned int mysql_get_timeout_value(const MYSQL *mysql); +unsigned int mysql_get_timeout_value_ms(const MYSQL *mysql); diff --git a/include/mysql_async.h b/include/mysql_async.h index 2c84d5bc1b6..2728b9c1dc7 100644 --- a/include/mysql_async.h +++ b/include/mysql_async.h @@ -20,14 +20,14 @@ extern int my_connect_async(struct mysql_async_context *b, my_socket fd, const struct sockaddr *name, uint namelen, - uint timeout); + int vio_timeout); extern ssize_t my_recv_async(struct mysql_async_context *b, int fd, - unsigned char *buf, size_t size, uint timeout); + unsigned char *buf, size_t size, int timeout); extern ssize_t my_send_async(struct mysql_async_context *b, int fd, const unsigned char *buf, size_t size, - uint timeout); -extern my_bool my_poll_read_async(struct mysql_async_context *b, - uint timeout); + int timeout); +extern my_bool my_io_wait_async(struct mysql_async_context *b, + enum enum_vio_io_event event, int timeout); #ifdef HAVE_OPENSSL extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl, void *buf, int size); diff --git a/sql-common/client.c b/sql-common/client.c index 52a64a2d6f4..bb015760b0c 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2995,9 +2995,10 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, mysql->options.extension->async_context->active) { my_bool old_mode; + int vio_timeout= get_vio_connect_timeout(mysql); vio_blocking(net->vio, FALSE, &old_mode); return my_connect_async(mysql->options.extension->async_context, fd, - name, namelen, mysql->options.connect_timeout); + name, namelen, vio_timeout); } return my_connect(fd, name, namelen, mysql->options.connect_timeout); diff --git a/sql-common/mysql_async.c b/sql-common/mysql_async.c index c130eab5061..8f3a91e26fa 100644 --- a/sql-common/mysql_async.c +++ b/sql-common/mysql_async.c @@ -56,7 +56,7 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b, /* Asynchronous connect(); socket must already be set non-blocking. */ int my_connect_async(struct mysql_async_context *b, my_socket fd, - const struct sockaddr *name, uint namelen, uint timeout) + const struct sockaddr *name, uint namelen, int vio_timeout) { int res; size_socket s_err_size; @@ -90,9 +90,13 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, return res; #endif b->events_to_wait_for|= MYSQL_WAIT_WRITE; - b->timeout_value= timeout; - if (timeout) + if (vio_timeout >= 0) + { + b->timeout_value= vio_timeout; b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; + } + else + b->timeout_value= 0; if (b->suspend_resume_hook) (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); my_context_yield(&b->async_context); @@ -119,7 +123,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, ssize_t my_recv_async(struct mysql_async_context *b, int fd, - unsigned char *buf, size_t size, uint timeout) + unsigned char *buf, size_t size, int timeout) { ssize_t res; @@ -129,7 +133,7 @@ my_recv_async(struct mysql_async_context *b, int fd, if (res >= 0 || IS_BLOCKING_ERROR()) return res; b->events_to_wait_for= MYSQL_WAIT_READ; - if (timeout) + if (timeout >= 0) { b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; b->timeout_value= timeout; @@ -147,7 +151,7 @@ my_recv_async(struct mysql_async_context *b, int fd, ssize_t my_send_async(struct mysql_async_context *b, int fd, - const unsigned char *buf, size_t size, uint timeout) + const unsigned char *buf, size_t size, int timeout) { ssize_t res; @@ -157,7 +161,7 @@ my_send_async(struct mysql_async_context *b, int fd, if (res >= 0 || IS_BLOCKING_ERROR()) return res; b->events_to_wait_for= MYSQL_WAIT_WRITE; - if (timeout) + if (timeout >= 0) { b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; b->timeout_value= timeout; @@ -174,16 +178,33 @@ my_send_async(struct mysql_async_context *b, int fd, my_bool -my_poll_read_async(struct mysql_async_context *b, uint timeout) +my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event, + int timeout) { - b->events_to_wait_for= MYSQL_WAIT_READ | MYSQL_WAIT_TIMEOUT; - b->timeout_value= timeout; + switch (event) + { + case VIO_IO_EVENT_READ: + b->events_to_wait_for = MYSQL_WAIT_READ; + break; + case VIO_IO_EVENT_WRITE: + b->events_to_wait_for = MYSQL_WAIT_WRITE; + break; + case VIO_IO_EVENT_CONNECT: + b->events_to_wait_for = MYSQL_WAIT_WRITE | IF_WIN(0, MYSQL_WAIT_EXCEPT); + break; + } + + if (timeout >= 0) + { + b->events_to_wait_for |= MYSQL_WAIT_TIMEOUT; + b->timeout_value= timeout; + } if (b->suspend_resume_hook) (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); my_context_yield(&b->async_context); if (b->suspend_resume_hook) (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); - return (b->events_occured & MYSQL_WAIT_READ) ? 0 : 1; + return (b->events_occured & MYSQL_WAIT_TIMEOUT) ? 0 : 1; } @@ -239,9 +260,26 @@ my_ssl_write_async(struct mysql_async_context *b, SSL *ssl, } #endif /* HAVE_OPENSSL */ +/* + Legacy support of the MariaDB 5.5 version, where timeouts where only in + seconds resolution. Applications that use this will be asked to set a timeout + at the nearest higher whole-seconds value. +*/ unsigned int STDCALL mysql_get_timeout_value(const MYSQL *mysql) { + unsigned int timeout= mysql->options.extension->async_context->timeout_value; + /* Avoid overflow. */ + if (timeout > UINT_MAX - 999) + return (timeout - 1)/1000 + 1; + else + return (timeout+999)/1000; +} + + +unsigned int STDCALL +mysql_get_timeout_value_ms(const MYSQL *mysql) +{ return mysql->options.extension->async_context->timeout_value; } diff --git a/vio/viosocket.c b/vio/viosocket.c index f7ab95d3e6c..b13fc2d3a83 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -897,6 +897,21 @@ int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout) MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */ DBUG_ENTER("vio_io_wait"); + /* + Note that if zero timeout, then we will not block, so we do not need to + yield to calling application in the async case. + */ + if (timeout != 0 && vio->async_context && vio->async_context->active) + { + MYSQL_START_SOCKET_WAIT(locker, &state, vio->mysql_socket, + PSI_SOCKET_SELECT, 0); + ret= my_io_wait_async(vio->async_context, event, timeout); + if (ret == 0) + errno= SOCKET_ETIMEDOUT; + MYSQL_END_SOCKET_WAIT(locker, 0); + DBUG_RETURN(ret); + } + memset(&pfd, 0, sizeof(pfd)); pfd.fd= sd; @@ -957,6 +972,21 @@ int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout) MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */ DBUG_ENTER("vio_io_wait"); + /* + Note that if zero timeout, then we will not block, so we do not need to + yield to calling application in the async case. + */ + if (timeout != 0 && vio->async_context && vio->async_context->active) + { + MYSQL_START_SOCKET_WAIT(locker, &state, vio->mysql_socket, + PSI_SOCKET_SELECT, 0); + ret= my_io_wait_async(vio->async_context, event, timeout); + if (ret == 0) + WSASetLastError(SOCKET_ETIMEDOUT); + MYSQL_END_SOCKET_WAIT(locker, 0); + DBUG_RETURN(ret); + } + /* Convert the timeout, in milliseconds, to seconds and microseconds. */ if (timeout >= 0) { |