List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:March 19 2008 3:04pm
Subject:bk commit into 6.0 tree (davi:1.2601) BUG#10374
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of davi.  When davi does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2008-03-19 11:04:50-03:00, davi@stripped +8 -0
  Bug#10374 GET_LOCK does not let connection to close on the server side if it's aborted
  
  The problem is that the server doesn't detect aborted connections which
  are waiting on a lock or sleeping (user sleep), wasting system resources
  for a connection that is already dead.
  
  The solution is to peek at the connection every five seconds to verify if
  the connection is not aborted. A aborted connection is detect by polling
  the connection socket for available data to be read or end of file. In
  case of eof, the wait is aborted and the connection killed.

  include/violite.h@stripped, 2008-03-19 11:04:46-03:00, davi@stripped +1 -0
    Export vio_peek_read function.

  mysql-test/r/dirty_close.result@stripped, 2008-03-19 11:04:46-03:00, davi@stripped +10 -0
    Add test case result for Bug#10374

  mysql-test/t/dirty_close.test@stripped, 2008-03-19 11:04:46-03:00, davi@stripped +33 -0
    Add test case for Bug#10374

  sql/item_func.cc@stripped, 2008-03-19 11:04:47-03:00, davi@stripped +54 -8
    While waiting for a condition to be signaled, check if
    the connection is not broken every INTERRUPT_INTERVAL
    seconds.

  sql/mysqld.cc@stripped, 2008-03-19 11:04:47-03:00, davi@stripped +3 -0
    Add new kill reason.

  sql/sql_class.cc@stripped, 2008-03-19 11:04:47-03:00, davi@stripped +24 -0
    Add function which checks if the client connection was
    aborted.

  sql/sql_class.h@stripped, 2008-03-19 11:04:47-03:00, davi@stripped +4 -0
    Add function prototype and kill reason.

  vio/viosocket.c@stripped, 2008-03-19 11:04:47-03:00, davi@stripped +45 -4
    Add poll and peek functions for Windows and Unix.

diff -Nrup a/include/violite.h b/include/violite.h
--- a/include/violite.h	2008-01-24 18:15:58 -02:00
+++ b/include/violite.h	2008-03-19 11:04:46 -03:00
@@ -86,6 +86,7 @@ my_socket vio_fd(Vio*vio);
 /* Remote peer's address and name in text form */
 my_bool vio_peer_addr(Vio *vio, char *buf, uint16 *port, size_t buflen);
 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 -Nrup a/mysql-test/r/dirty_close.result b/mysql-test/r/dirty_close.result
--- a/mysql-test/r/dirty_close.result	2002-02-11 20:22:55 -02:00
+++ b/mysql-test/r/dirty_close.result	2008-03-19 11:04:46 -03:00
@@ -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 -Nrup a/mysql-test/t/dirty_close.test b/mysql-test/t/dirty_close.test
--- a/mysql-test/t/dirty_close.test	2005-07-27 21:21:40 -03:00
+++ b/mysql-test/t/dirty_close.test	2008-03-19 11:04:46 -03:00
@@ -12,5 +12,38 @@ create table t1 (n int);
 insert into t1 values (1),(2),(3);
 select * from t1;
 drop table t1;
+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;
diff -Nrup a/sql/item_func.cc b/sql/item_func.cc
--- a/sql/item_func.cc	2008-03-12 07:54:17 -03:00
+++ b/sql/item_func.cc	2008-03-19 11:04:47 -03:00
@@ -3455,6 +3455,55 @@ void debug_sync_point(const char* lock_n
 
 #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
+
+static int interruptible_wait(THD *thd, pthread_cond_t *cond,
+                              pthread_mutex_t *lock, ulonglong timeout)
+{
+  int error;
+  ulonglong slice;
+  struct timespec abstime;
+
+  do
+  {
+    /* Wait for a fixed interval. */
+    if (timeout > INTERRUPT_INTERVAL)
+      slice= INTERRUPT_INTERVAL;
+    else
+      slice= timeout;
+
+    timeout-= slice;
+    set_timespec(abstime, slice);
+    error= pthread_cond_timedwait(cond, lock, &abstime);
+    if (error == ETIMEDOUT || error == ETIME)
+    {
+      /* Timed out */
+      if (!timeout)
+        break;
+
+      /* Check if connection is alive. */
+      if (thd->vio_is_connected())
+        continue;
+
+      thd->killed= THD::KILL_BROKEN_CONNECTION;
+      break;
+   }
+  } while (error && timeout);
+
+  return error;
+}
+
 /**
   Get a user level lock.  If the thread has an old lock this is first released.
 
@@ -3470,8 +3519,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;
+  ulonglong timeout=args[1]->val_int();
   THD *thd=current_thd;
   User_level_lock *ull;
   int error;
@@ -3535,12 +3583,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"));
@@ -3735,14 +3782,13 @@ void Item_func_benchmark::print(String *
 longlong Item_func_sleep::val_int()
 {
   THD *thd= current_thd;
-  struct timespec abstime;
   pthread_cond_t cond;
+  ulonglong timeout;
   int error;
 
   DBUG_ASSERT(fixed == 1);
 
-  double time= args[0]->val_real();
-  set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000)));
+  timeout= args[0]->val_int();
 
   pthread_cond_init(&cond, NULL);
   pthread_mutex_lock(&LOCK_user_locks);
@@ -3753,7 +3799,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 -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
--- a/sql/mysqld.cc	2008-03-19 09:16:03 -03:00
+++ b/sql/mysqld.cc	2008-03-19 11:04:47 -03:00
@@ -2558,6 +2558,9 @@ terribly wrong...\n");  
     case THD::KILLED_NO_VALUE:
       kreason= "KILLED_NO_VALUE";
       break;
+    case THD::KILL_BROKEN_CONNECTION:
+      kreason= "THD::KILL_BROKEN_CONNECTION";
+      break;
     }
     fprintf(stderr, "Trying to get some variables.\n\
 Some pointers may be invalid and cause the dump to abort...\n");
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2008-03-18 07:54:34 -03:00
+++ b/sql/sql_class.cc	2008-03-19 11:04:47 -03:00
@@ -1451,6 +1451,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 -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2008-03-18 07:54:34 -03:00
+++ b/sql/sql_class.h	2008-03-19 11:04:47 -03:00
@@ -1658,6 +1658,7 @@ public:
     KILL_BAD_DATA=1,
     KILL_CONNECTION=ER_SERVER_SHUTDOWN,
     KILL_QUERY=ER_QUERY_INTERRUPTED,
+    KILL_BROKEN_CONNECTION,
     KILLED_NO_VALUE      /* means neither of the states */
   };
   killed_state volatile killed;
@@ -1955,9 +1956,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_is_connected() { return true; }
 #endif
   /**
     Mark the current error as fatal. Warning: this does not
diff -Nrup a/vio/viosocket.c b/vio/viosocket.c
--- a/vio/viosocket.c	2008-01-24 18:15:41 -02:00
+++ b/vio/viosocket.c	2008-03-19 11:04:47 -03:00
@@ -360,9 +360,24 @@ my_bool vio_peer_addr(Vio *vio, char *bu
 
 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,10 +388,36 @@ my_bool vio_poll_read(Vio *vio,uint time
   {
     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)
 {
Thread
bk commit into 6.0 tree (davi:1.2601) BUG#10374Davi Arnaut19 Mar