summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <knielsen@knielsen-hq.org>2012-10-12 11:00:01 +0200
committerunknown <knielsen@knielsen-hq.org>2012-10-12 11:00:01 +0200
commit52c84d144d3b07966d9b3bab8694eb012eef69ce (patch)
tree2419ab191bc656e3355fdd38b8cf274f68f4a049
parentd7e0499407aa684f1d09cf295dba9f912b74fd3b (diff)
downloadmariadb-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.h4
-rw-r--r--include/mysql.h1
-rw-r--r--include/mysql.h.pp1
-rw-r--r--include/mysql_async.h10
-rw-r--r--sql-common/client.c3
-rw-r--r--sql-common/mysql_async.c60
-rw-r--r--vio/viosocket.c30
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)
{