diff options
-rw-r--r-- | include/violite.h | 1 | ||||
-rw-r--r-- | mysql-test/r/dirty_close.result | 10 | ||||
-rw-r--r-- | mysql-test/t/dirty_close.test | 32 | ||||
-rw-r--r-- | sql/item_func.cc | 58 | ||||
-rw-r--r-- | sql/sql_class.cc | 24 | ||||
-rw-r--r-- | sql/sql_class.h | 5 | ||||
-rw-r--r-- | vio/viosocket.c | 49 |
7 files changed, 164 insertions, 15 deletions
diff --git a/include/violite.h b/include/violite.h index f833606233c..0e07e1c69ca 100644 --- a/include/violite.h +++ b/include/violite.h @@ -88,6 +88,7 @@ my_bool vio_peer_addr(Vio* vio, char *buf, uint16 *port); /* Remotes in_addr */ void vio_in_addr(Vio *vio, struct in_addr *in); my_bool vio_poll_read(Vio *vio,uint timeout); +my_bool vio_peek_read(Vio *vio, uint *bytes); #ifdef HAVE_OPENSSL #include <openssl/opensslv.h> diff --git a/mysql-test/r/dirty_close.result b/mysql-test/r/dirty_close.result index b49b72f1b95..f7012ff9c01 100644 --- a/mysql-test/r/dirty_close.result +++ b/mysql-test/r/dirty_close.result @@ -7,3 +7,13 @@ n 2 3 DROP TABLE t1; +SELECT GET_LOCK("dangling", 0); +GET_LOCK("dangling", 0) +1 +SELECT GET_LOCK('dangling', 3600);; +SELECT GET_LOCK('dangling', 3600);; +SELECT RELEASE_LOCK('dangling'); +RELEASE_LOCK('dangling') +1 +GET_LOCK('dangling', 3600) +1 diff --git a/mysql-test/t/dirty_close.test b/mysql-test/t/dirty_close.test index 1bbd53e8c06..17a729000e3 100644 --- a/mysql-test/t/dirty_close.test +++ b/mysql-test/t/dirty_close.test @@ -22,6 +22,38 @@ disconnect con2; # End of 4.1 tests +# +# Bug#10374 GET_LOCK does not let connection to close on the server side if it's aborted +# + +connection default; +SELECT GET_LOCK("dangling", 0); +connect(con1, localhost, root,,); +connection con1; +--send SELECT GET_LOCK('dangling', 3600); +connection default; +let $wait_condition= + SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = "User lock" + AND INFO = "SELECT GET_LOCK('dangling', 3600)"; +--source include/wait_condition.inc +dirty_close con1; +let $wait_condition= + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = "User lock" + AND INFO = "SELECT GET_LOCK('dangling', 3600)"; +--source include/wait_condition.inc +connect(con1, localhost, root,,); +--send SELECT GET_LOCK('dangling', 3600); +connection default; +let $wait_condition= + SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = "User lock" + AND INFO = "SELECT GET_LOCK('dangling', 3600)"; +--source include/wait_condition.inc +SELECT RELEASE_LOCK('dangling'); +connection con1; +--reap +connection default; +disconnect con1; + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/item_func.cc b/sql/item_func.cc index f4db3ef03e7..8f2739f8e77 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3437,6 +3437,48 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) #endif + +/** + Wait for a given condition to be signaled within the specified timeout. + + @param cond the condition variable to wait on + @param lock the associated mutex + @param abstime the amount of time in seconds to wait + + @retval return value from pthread_cond_timedwait +*/ + +#define INTERRUPT_INTERVAL (5 * ULL(1000000000)) + +static int interruptible_wait(THD *thd, pthread_cond_t *cond, + pthread_mutex_t *lock, double time) +{ + int error; + struct timespec abstime; + ulonglong slice, timeout= (ulonglong) (time * 1000000000.0); + + do + { + /* Wait for a fixed interval. */ + if (timeout > INTERRUPT_INTERVAL) + slice= INTERRUPT_INTERVAL; + else + slice= timeout; + + timeout-= slice; + set_timespec_nsec(abstime, slice); + error= pthread_cond_timedwait(cond, lock, &abstime); + if (error == ETIMEDOUT || error == ETIME) + { + /* Return error if timed out or connection is broken. */ + if (!timeout || !thd->vio_is_connected()) + break; + } + } while (error && timeout); + + return error; +} + /** Get a user level lock. If the thread has an old lock this is first released. @@ -3452,8 +3494,7 @@ longlong Item_func_get_lock::val_int() { DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); - longlong timeout=args[1]->val_int(); - struct timespec abstime; + double timeout= args[1]->val_real(); THD *thd=current_thd; User_level_lock *ull; int error; @@ -3517,12 +3558,11 @@ longlong Item_func_get_lock::val_int() thd->mysys_var->current_mutex= &LOCK_user_locks; thd->mysys_var->current_cond= &ull->cond; - set_timespec(abstime,timeout); error= 0; while (ull->locked && !thd->killed) { DBUG_PRINT("info", ("waiting on lock")); - error= pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime); + error= interruptible_wait(thd, &ull->cond, &LOCK_user_locks, timeout); if (error == ETIMEDOUT || error == ETIME) { DBUG_PRINT("info", ("lock wait timeout")); @@ -3717,13 +3757,13 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type) longlong Item_func_sleep::val_int() { THD *thd= current_thd; - struct timespec abstime; pthread_cond_t cond; + double timeout; int error; DBUG_ASSERT(fixed == 1); - double time= args[0]->val_real(); + timeout= args[0]->val_real(); /* On 64-bit OSX pthread_cond_timedwait() waits forever if passed abstime time has already been exceeded by @@ -3733,10 +3773,8 @@ longlong Item_func_sleep::val_int() We assume that the lines between this test and the call to pthread_cond_timedwait() will be executed in less than 0.00001 sec. */ - if (time < 0.00001) + if (timeout < 0.00001) return 0; - - set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000))); pthread_cond_init(&cond, NULL); pthread_mutex_lock(&LOCK_user_locks); @@ -3748,7 +3786,7 @@ longlong Item_func_sleep::val_int() error= 0; while (!thd->killed) { - error= pthread_cond_timedwait(&cond, &LOCK_user_locks, &abstime); + error= interruptible_wait(thd, &cond, &LOCK_user_locks, timeout); if (error == ETIMEDOUT || error == ETIME) break; error= 0; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 212d727b7f1..e765d892884 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1618,6 +1618,30 @@ void THD::rollback_item_tree_changes() } +#ifndef EMBEDDED_LIBRARY + +/** + Check that the endpoint is still available. +*/ + +bool THD::vio_is_connected() +{ + uint bytes= 0; + + /* End of input is signaled by poll if the socket is aborted. */ + if (vio_poll_read(net.vio, 0)) + return TRUE; + + /* Socket is aborted if signaled but no data is available. */ + if (vio_peek_read(net.vio, &bytes)) + return TRUE; + + return bytes ? TRUE : FALSE; +} + +#endif + + /***************************************************************************** ** Functions to provide a interface to select results *****************************************************************************/ diff --git a/sql/sql_class.h b/sql/sql_class.h index 3fe82429492..5117304a26f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1961,9 +1961,12 @@ public: DBUG_VOID_RETURN; } inline bool vio_ok() const { return net.vio != 0; } + /** Return FALSE if connection to client is broken. */ + bool vio_is_connected(); #else void clear_error(); - inline bool vio_ok() const { return true; } + inline bool vio_ok() const { return TRUE; } + inline bool vio_is_connected() { return TRUE; } #endif /** Mark the current error as fatal. Warning: this does not diff --git a/vio/viosocket.c b/vio/viosocket.c index 2a22f8c7c15..e83559729b9 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -360,9 +360,24 @@ void vio_in_addr(Vio *vio, struct in_addr *in) my_bool vio_poll_read(Vio *vio,uint timeout) { -#ifndef HAVE_POLL - return 0; -#else +#ifdef __WIN__ + int res, fd= vio->sd; + fd_set readfds, errorfds; + struct timeval tm; + DBUG_ENTER("vio_poll"); + tm.tv_sec= timeout; + tm.tv_usec= 0; + FD_ZERO(&readfds); + FD_ZERO(&errorfds); + FD_SET(fd, &readfds); + FD_SET(fd, &errorfds); + if ((res= select(fd, &readfds, NULL, &errorfds, &tm) <= 0)) + { + DBUG_RETURN(res < 0 ? 0 : 1); + } + res= FD_ISSET(fd, &readfds) || FD_ISSET(fd, &errorfds); + DBUG_RETURN(!res); +#elif defined(HAVE_POLL) struct pollfd fds; int res; DBUG_ENTER("vio_poll"); @@ -373,11 +388,37 @@ my_bool vio_poll_read(Vio *vio,uint timeout) { DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */ } - DBUG_RETURN(fds.revents & POLLIN ? 0 : 1); + DBUG_RETURN(fds.revents & (POLLIN | POLLERR | POLLHUP) ? 0 : 1); +#else + return 0; #endif } +my_bool vio_peek_read(Vio *vio, uint *bytes) +{ +#ifdef __WIN__ + int len; + if (ioctlsocket(vio->sd, FIONREAD, &len)) + return TRUE; + *bytes= len; + return FALSE; +#elif FIONREAD + int len; + if (ioctl(vio->sd, FIONREAD, &len) < 0) + return TRUE; + *bytes= len; + return FALSE; +#else + char buf[1024]; + ssize_t res= recv(vio->sd, &buf, sizeof(buf), MSG_PEEK); + if (res < 0) + return TRUE; + *bytes= res; + return FALSE; +#endif +} + void vio_timeout(Vio *vio, uint which, uint timeout) { #if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO) |