Commit 52c84d14 authored by unknown's avatar unknown

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.
parent d7e04994
...@@ -182,8 +182,8 @@ struct mysql_async_context { ...@@ -182,8 +182,8 @@ struct mysql_async_context {
my_bool r_my_bool; my_bool r_my_bool;
} ret_result; } ret_result;
/* /*
The timeout value, for suspended calls that need to wake up on a timeout The timeout value (in millisecods), for suspended calls that need to wake
(eg. mysql_real_connect_start(). up on a timeout (eg. mysql_real_connect_start().
*/ */
unsigned int timeout_value; unsigned int timeout_value;
/* /*
......
...@@ -852,6 +852,7 @@ int STDCALL mysql_close_start(MYSQL *sock); ...@@ -852,6 +852,7 @@ int STDCALL mysql_close_start(MYSQL *sock);
int STDCALL mysql_close_cont(MYSQL *sock, int status); int STDCALL mysql_close_cont(MYSQL *sock, int status);
my_socket STDCALL mysql_get_socket(const MYSQL *mysql); 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(const MYSQL *mysql);
unsigned int STDCALL mysql_get_timeout_value_ms(const MYSQL *mysql);
/* status return codes */ /* status return codes */
#define MYSQL_NO_DATA 100 #define MYSQL_NO_DATA 100
......
...@@ -733,3 +733,4 @@ int mysql_close_start(MYSQL *sock); ...@@ -733,3 +733,4 @@ int mysql_close_start(MYSQL *sock);
int mysql_close_cont(MYSQL *sock, int status); int mysql_close_cont(MYSQL *sock, int status);
my_socket mysql_get_socket(const MYSQL *mysql); my_socket mysql_get_socket(const MYSQL *mysql);
unsigned int mysql_get_timeout_value(const MYSQL *mysql); unsigned int mysql_get_timeout_value(const MYSQL *mysql);
unsigned int mysql_get_timeout_value_ms(const MYSQL *mysql);
...@@ -20,14 +20,14 @@ ...@@ -20,14 +20,14 @@
extern int my_connect_async(struct mysql_async_context *b, my_socket fd, extern int my_connect_async(struct mysql_async_context *b, my_socket fd,
const struct sockaddr *name, uint namelen, const struct sockaddr *name, uint namelen,
uint timeout); int vio_timeout);
extern ssize_t my_recv_async(struct mysql_async_context *b, int fd, 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, extern ssize_t my_send_async(struct mysql_async_context *b, int fd,
const unsigned char *buf, size_t size, const unsigned char *buf, size_t size,
uint timeout); int timeout);
extern my_bool my_poll_read_async(struct mysql_async_context *b, extern my_bool my_io_wait_async(struct mysql_async_context *b,
uint timeout); enum enum_vio_io_event event, int timeout);
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl, extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl,
void *buf, int size); void *buf, int size);
......
...@@ -2995,9 +2995,10 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, ...@@ -2995,9 +2995,10 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd,
mysql->options.extension->async_context->active) mysql->options.extension->async_context->active)
{ {
my_bool old_mode; my_bool old_mode;
int vio_timeout= get_vio_connect_timeout(mysql);
vio_blocking(net->vio, FALSE, &old_mode); vio_blocking(net->vio, FALSE, &old_mode);
return my_connect_async(mysql->options.extension->async_context, fd, 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); return my_connect(fd, name, namelen, mysql->options.connect_timeout);
......
...@@ -56,7 +56,7 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b, ...@@ -56,7 +56,7 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b,
/* Asynchronous connect(); socket must already be set non-blocking. */ /* Asynchronous connect(); socket must already be set non-blocking. */
int int
my_connect_async(struct mysql_async_context *b, my_socket fd, 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; int res;
size_socket s_err_size; size_socket s_err_size;
...@@ -90,9 +90,13 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, ...@@ -90,9 +90,13 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
return res; return res;
#endif #endif
b->events_to_wait_for|= MYSQL_WAIT_WRITE; b->events_to_wait_for|= MYSQL_WAIT_WRITE;
b->timeout_value= timeout; if (vio_timeout >= 0)
if (timeout) {
b->timeout_value= vio_timeout;
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
}
else
b->timeout_value= 0;
if (b->suspend_resume_hook) if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context); my_context_yield(&b->async_context);
...@@ -119,7 +123,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd, ...@@ -119,7 +123,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
ssize_t ssize_t
my_recv_async(struct mysql_async_context *b, int fd, 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; ssize_t res;
...@@ -129,7 +133,7 @@ my_recv_async(struct mysql_async_context *b, int fd, ...@@ -129,7 +133,7 @@ my_recv_async(struct mysql_async_context *b, int fd,
if (res >= 0 || IS_BLOCKING_ERROR()) if (res >= 0 || IS_BLOCKING_ERROR())
return res; return res;
b->events_to_wait_for= MYSQL_WAIT_READ; b->events_to_wait_for= MYSQL_WAIT_READ;
if (timeout) if (timeout >= 0)
{ {
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout; b->timeout_value= timeout;
...@@ -147,7 +151,7 @@ my_recv_async(struct mysql_async_context *b, int fd, ...@@ -147,7 +151,7 @@ my_recv_async(struct mysql_async_context *b, int fd,
ssize_t ssize_t
my_send_async(struct mysql_async_context *b, int fd, 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; ssize_t res;
...@@ -157,7 +161,7 @@ my_send_async(struct mysql_async_context *b, int fd, ...@@ -157,7 +161,7 @@ my_send_async(struct mysql_async_context *b, int fd,
if (res >= 0 || IS_BLOCKING_ERROR()) if (res >= 0 || IS_BLOCKING_ERROR())
return res; return res;
b->events_to_wait_for= MYSQL_WAIT_WRITE; b->events_to_wait_for= MYSQL_WAIT_WRITE;
if (timeout) if (timeout >= 0)
{ {
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout; b->timeout_value= timeout;
...@@ -174,16 +178,33 @@ my_send_async(struct mysql_async_context *b, int fd, ...@@ -174,16 +178,33 @@ my_send_async(struct mysql_async_context *b, int fd,
my_bool 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; 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; b->timeout_value= timeout;
}
if (b->suspend_resume_hook) if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context); my_context_yield(&b->async_context);
if (b->suspend_resume_hook) if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); (*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,8 +260,25 @@ my_ssl_write_async(struct mysql_async_context *b, SSL *ssl, ...@@ -239,8 +260,25 @@ my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
} }
#endif /* HAVE_OPENSSL */ #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 unsigned int STDCALL
mysql_get_timeout_value(const MYSQL *mysql) 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; return mysql->options.extension->async_context->timeout_value;
} }
......
...@@ -897,6 +897,21 @@ int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout) ...@@ -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 ';' */ MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */
DBUG_ENTER("vio_io_wait"); 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)); memset(&pfd, 0, sizeof(pfd));
pfd.fd= sd; pfd.fd= sd;
...@@ -957,6 +972,21 @@ int vio_io_wait(Vio *vio, enum enum_vio_io_event event, int timeout) ...@@ -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 ';' */ MYSQL_SOCKET_WAIT_VARIABLES(locker, state) /* no ';' */
DBUG_ENTER("vio_io_wait"); 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. */ /* Convert the timeout, in milliseconds, to seconds and microseconds. */
if (timeout >= 0) if (timeout >= 0)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment