summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/violite.h1
-rw-r--r--mysql-test/r/dirty_close.result10
-rw-r--r--mysql-test/t/dirty_close.test32
-rw-r--r--sql/item_func.cc58
-rw-r--r--sql/sql_class.cc24
-rw-r--r--sql/sql_class.h5
-rw-r--r--vio/viosocket.c49
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)