From c8a6c2c6143dd8c92704920d455cc57050ce71ad Mon Sep 17 00:00:00 2001
From: "jimw@mysql.com" <>
Date: Tue, 11 Oct 2005 09:12:12 -0700
Subject: [PATCH] Fix wait_timeout (and kill) handling on Mac OS X by cleaning
 up how signal handlers are set up, the blocking flags for sockets are set,
 and which thread-related functions are used. (Bug #8731)

---
 configure.in                         | 16 +++---------
 include/config-win.h                 |  2 +-
 include/my_pthread.h                 | 35 +++++++++++++++-----------
 mysql-test/r/wait_timeout.result     |  8 ++++++
 mysql-test/t/wait_timeout-master.opt |  1 +
 mysql-test/t/wait_timeout.test       | 11 +++++++++
 mysys/my_pthread.c                   | 17 -------------
 mysys/thr_alarm.c                    | 24 ++++++------------
 sql/mysqld.cc                        | 37 ++++------------------------
 vio/vio.c                            | 16 +++++++++---
 vio/viosocket.c                      |  9 ++++++-
 11 files changed, 77 insertions(+), 99 deletions(-)
 create mode 100644 mysql-test/r/wait_timeout.result
 create mode 100644 mysql-test/t/wait_timeout-master.opt
 create mode 100644 mysql-test/t/wait_timeout.test

diff --git a/configure.in b/configure.in
index 97eb0cb0edf..44bfcb46746 100644
--- a/configure.in
+++ b/configure.in
@@ -1102,7 +1102,7 @@ case $SYSTEM_TYPE in
   *darwin5*)
     if test "$ac_cv_prog_gcc" = "yes"
     then
-      FLAGS="-traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH"
+      FLAGS="-traditional-cpp -DHAVE_DARWIN5_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH"
       CFLAGS="$CFLAGS $FLAGS"
       CXXFLAGS="$CXXFLAGS $FLAGS"
       MAX_C_OPTIMIZE="-O"
@@ -1112,23 +1112,13 @@ case $SYSTEM_TYPE in
   *darwin6*)
     if test "$ac_cv_prog_gcc" = "yes"
     then
-      FLAGS="-DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH"
+      FLAGS="-D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH"
       CFLAGS="$CFLAGS $FLAGS"
       CXXFLAGS="$CXXFLAGS $FLAGS"
       MAX_C_OPTIMIZE="-O"
     fi
     ;;
-  *darwin[[7-8]]*)
-    # don't forget to escape [] like above
-    if test "$ac_cv_prog_gcc" = "yes"
-    then
-      FLAGS="-DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DIGNORE_SIGHUP_SIGQUIT"
-      CFLAGS="$CFLAGS $FLAGS"
-      CXXFLAGS="$CXXFLAGS $FLAGS"
-      MAX_C_OPTIMIZE="-O"
-    fi
-    ;;
-  *darwin9*)
+  *darwin*)
     if test "$ac_cv_prog_gcc" = "yes"
     then
       FLAGS="-D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DIGNORE_SIGHUP_SIGQUIT"
diff --git a/include/config-win.h b/include/config-win.h
index 0899d961d14..9663947683e 100644
--- a/include/config-win.h
+++ b/include/config-win.h
@@ -192,7 +192,7 @@ typedef uint rf_SetTimer;
 
 /* Convert some simple functions to Posix */
 
-#define sigset(A,B) signal((A),(B))
+#define my_sigset(A,B) signal((A),(B))
 #define finite(A) _finite(A)
 #define sleep(A)  Sleep((A)*1000)
 #define popen(A) popen(A,B) _popen((A),(B))
diff --git a/include/my_pthread.h b/include/my_pthread.h
index d83ddf62a80..b6b65d4389a 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -289,8 +289,6 @@ extern int my_pthread_create_detached;
 #undef HAVE_PTHREAD_RWLOCK_RDLOCK
 #undef HAVE_SNPRINTF
 
-#define sigset(A,B) pthread_signal((A),(void (*)(int)) (B))
-#define signal(A,B) pthread_signal((A),(void (*)(int)) (B))
 #define my_pthread_attr_setprio(A,B)
 #endif /* defined(PTHREAD_SCOPE_GLOBAL) && !defined(PTHREAD_SCOPE_SYSTEM) */
 
@@ -322,14 +320,26 @@ extern int my_pthread_cond_init(pthread_cond_t *mp,
 #if !defined(HAVE_SIGWAIT) && !defined(HAVE_mit_thread) && !defined(HAVE_rts_threads) && !defined(sigwait) && !defined(alpha_linux_port) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) && !defined(_AIX)
 int sigwait(sigset_t *setp, int *sigp);		/* Use our implemention */
 #endif
-#if !defined(HAVE_SIGSET) && !defined(HAVE_mit_thread) && !defined(sigset)
-#define sigset(A,B) do { struct sigaction s; sigset_t set;              \
-                         sigemptyset(&set);                             \
-                         s.sa_handler = (B);                            \
-                         s.sa_mask    = set;                            \
-                         s.sa_flags   = 0;                              \
-                         sigaction((A), &s, (struct sigaction *) NULL); \
-                       } while (0)
+
+
+/*
+  We define my_sigset() and use that instead of the system sigset() so that
+  we can favor an implementation based on sigaction(). On some systems, such
+  as Mac OS X, sigset() results in flags such as SA_RESTART being set, and
+  we want to make sure that no such flags are set.
+*/
+#if defined(HAVE_SIGACTION) && !defined(my_sigset)
+#define my_sigset(A,B) do { struct sigaction s; sigset_t set;              \
+                            sigemptyset(&set);                             \
+                            s.sa_handler = (B);                            \
+                            s.sa_mask    = set;                            \
+                            s.sa_flags   = 0;                              \
+                            sigaction((A), &s, (struct sigaction *) NULL); \
+                          } while (0)
+#elif defined(HAVE_SIGSET) && !defined(my_sigset)
+#define my_sigset(A,B) sigset((A),(B))
+#elif !defined(my_sigset)
+#define my_sigset(A,B) signal((A),(B))
 #endif
 
 #ifndef my_pthread_setprio
@@ -409,16 +419,13 @@ struct tm *gmtime_r(const time_t *clock, struct tm *res);
 #define pthread_detach_this_thread() { pthread_t tmp=pthread_self() ; pthread_detach(&tmp); }
 #endif
 
-#ifdef HAVE_DARWIN_THREADS
+#ifdef HAVE_DARWIN5_THREADS
 #define pthread_sigmask(A,B,C) sigprocmask((A),(B),(C))
 #define pthread_kill(A,B) pthread_dummy(0)
 #define pthread_condattr_init(A) pthread_dummy(0)
 #define pthread_condattr_destroy(A) pthread_dummy(0)
-#define pthread_signal(A,B) pthread_dummy(0)
 #undef	pthread_detach_this_thread
 #define pthread_detach_this_thread() { pthread_t tmp=pthread_self() ; pthread_detach(tmp); }
-#undef sigset
-#define sigset(A,B) pthread_signal((A),(void (*)(int)) (B))
 #endif
 
 #if ((defined(HAVE_PTHREAD_ATTR_CREATE) && !defined(HAVE_SIGWAIT)) || defined(HAVE_DEC_3_2_THREADS)) && !defined(HAVE_CTHREADS_WRAPPER)
diff --git a/mysql-test/r/wait_timeout.result b/mysql-test/r/wait_timeout.result
new file mode 100644
index 00000000000..56232e481c0
--- /dev/null
+++ b/mysql-test/r/wait_timeout.result
@@ -0,0 +1,8 @@
+select 1;
+1
+1
+select 2;
+ERROR HY000: MySQL server has gone away
+select 3;
+3
+3
diff --git a/mysql-test/t/wait_timeout-master.opt b/mysql-test/t/wait_timeout-master.opt
new file mode 100644
index 00000000000..0ad622e9677
--- /dev/null
+++ b/mysql-test/t/wait_timeout-master.opt
@@ -0,0 +1 @@
+--wait-timeout=2
diff --git a/mysql-test/t/wait_timeout.test b/mysql-test/t/wait_timeout.test
new file mode 100644
index 00000000000..26f91569868
--- /dev/null
+++ b/mysql-test/t/wait_timeout.test
@@ -0,0 +1,11 @@
+#
+# Bug #8731: wait_timeout does not work on Mac OS X
+#
+--disable_reconnect
+select 1;
+# wait_timeout is 2, so we should get disconnected now
+--sleep 5
+--error 2006
+select 2;
+--enable_reconnect
+select 3;
diff --git a/mysys/my_pthread.c b/mysys/my_pthread.c
index 37517fb8327..315e966bf43 100644
--- a/mysys/my_pthread.c
+++ b/mysys/my_pthread.c
@@ -404,23 +404,6 @@ int sigwait(sigset_t *setp, int *sigp)
 #endif /* DONT_USE_SIGSUSPEND */
 #endif /* HAVE_SIGWAIT */
 
-/*****************************************************************************
-** Implement pthread_signal for systems that can't use signal() with threads
-** Currently this is only used with BSDI 3.0
-*****************************************************************************/
-
-#ifdef USE_PTHREAD_SIGNAL
-
-int pthread_signal(int sig, void (*func)())
-{
-  struct sigaction sact;
-  sact.sa_flags= 0;
-  sact.sa_handler= func;
-  sigemptyset(&sact.sa_mask);
-  sigaction(sig, &sact, (struct sigaction*) 0);
-  return 0;
-}
-#endif
 
 /****************************************************************************
  The following functions fixes that all pthread functions should work
diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c
index 05d14073953..e5b77de5e38 100644
--- a/mysys/thr_alarm.c
+++ b/mysys/thr_alarm.c
@@ -80,17 +80,7 @@ void init_thr_alarm(uint max_alarms)
   pthread_mutex_init(&LOCK_alarm,MY_MUTEX_INIT_FAST);
   pthread_cond_init(&COND_alarm,NULL);
 #if THR_CLIENT_ALARM != SIGALRM || defined(USE_ALARM_THREAD)
-#if defined(HAVE_mit_thread)
-  sigset(THR_CLIENT_ALARM,thread_alarm);	/* int. thread system calls */
-#else
-  {
-    struct sigaction sact;
-    sact.sa_flags = 0;
-    bzero((char*) &sact, sizeof(sact));
-    sact.sa_handler = thread_alarm;
-    sigaction(THR_CLIENT_ALARM, &sact, (struct sigaction*) 0);
-  }
-#endif
+  my_sigset(THR_CLIENT_ALARM,thread_alarm);
 #endif
   sigemptyset(&s);
   sigaddset(&s, THR_SERVER_ALARM);
@@ -110,12 +100,12 @@ void init_thr_alarm(uint max_alarms)
 #elif defined(USE_ONE_SIGNAL_HAND)
   pthread_sigmask(SIG_BLOCK, &s, NULL);		/* used with sigwait() */
 #if THR_SERVER_ALARM == THR_CLIENT_ALARM
-  sigset(THR_CLIENT_ALARM,process_alarm);	/* Linuxthreads */
+  my_sigset(THR_CLIENT_ALARM,process_alarm);	/* Linuxthreads */
   pthread_sigmask(SIG_UNBLOCK, &s, NULL);
 #endif
 #else
+  my_sigset(THR_SERVER_ALARM, process_alarm);
   pthread_sigmask(SIG_UNBLOCK, &s, NULL);
-  sigset(THR_SERVER_ALARM,process_alarm);
 #endif
   DBUG_VOID_RETURN;
 }
@@ -290,7 +280,7 @@ sig_handler process_alarm(int sig __attribute__((unused)))
     printf("thread_alarm\n"); fflush(stdout);
 #endif
 #ifdef DONT_REMEMBER_SIGNAL
-    sigset(THR_CLIENT_ALARM,process_alarm);	/* int. thread system calls */
+    my_sigset(THR_CLIENT_ALARM,process_alarm);	/* int. thread system calls */
 #endif
     return;
   }
@@ -310,7 +300,7 @@ sig_handler process_alarm(int sig __attribute__((unused)))
   process_alarm_part2(sig);
 #ifndef USE_ALARM_THREAD
 #if defined(DONT_REMEMBER_SIGNAL) && !defined(USE_ONE_SIGNAL_HAND)
-  sigset(THR_SERVER_ALARM,process_alarm);
+  my_sigset(THR_SERVER_ALARM,process_alarm);
 #endif
   pthread_mutex_unlock(&LOCK_alarm);
   pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
@@ -512,7 +502,7 @@ static sig_handler thread_alarm(int sig)
   printf("thread_alarm\n"); fflush(stdout);
 #endif
 #ifdef DONT_REMEMBER_SIGNAL
-  sigset(sig,thread_alarm);		/* int. thread system calls */
+  my_sigset(sig,thread_alarm);		/* int. thread system calls */
 #endif
 }
 #endif
@@ -916,7 +906,7 @@ static sig_handler print_signal_warning(int sig)
   printf("Warning: Got signal %d from thread %s\n",sig,my_thread_name());
   fflush(stdout);
 #ifdef DONT_REMEMBER_SIGNAL
-  sigset(sig,print_signal_warning);		/* int. thread system calls */
+  my_sigset(sig,print_signal_warning);		/* int. thread system calls */
 #endif
 #ifndef OS2
   if (sig == SIGALRM)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a6a91ac32ee..feb63e10b9f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -846,7 +846,7 @@ static void __cdecl kill_server(int sig_ptr)
     RETURN_FROM_KILL_SERVER;
   kill_in_progress=TRUE;
   abort_loop=1;					// This should be set
-  signal(sig,SIG_IGN);
+  my_sigset(sig,SIG_IGN);
   if (sig == MYSQL_KILL_SIGNAL || sig == 0)
     sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname);
   else
@@ -894,11 +894,6 @@ extern "C" pthread_handler_decl(kill_server_thread,arg __attribute__((unused)))
 }
 #endif
 
-#if defined(__amiga__)
-#undef sigset
-#define sigset signal
-#endif
-
 extern "C" sig_handler print_signal_warning(int sig)
 {
   if (!DBUG_IN_USE)
@@ -908,7 +903,7 @@ extern "C" sig_handler print_signal_warning(int sig)
 		      sig,my_thread_id());
   }
 #ifdef DONT_REMEMBER_SIGNAL
-  sigset(sig,print_signal_warning);		/* int. thread system calls */
+  my_sigset(sig,print_signal_warning);		/* int. thread system calls */
 #endif
 #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
   if (sig == SIGALRM)
@@ -1570,23 +1565,6 @@ void flush_thread_cache()
 }
 
 
-/*
-  Aborts a thread nicely. Commes here on SIGPIPE
-  TODO: One should have to fix that thr_alarm know about this
-  thread too.
-*/
-
-#ifdef THREAD_SPECIFIC_SIGPIPE
-extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
-{
-  THD *thd=current_thd;
-  DBUG_ENTER("abort_thread");
-  if (thd)
-    thd->killed=1;
-  DBUG_VOID_RETURN;
-}
-#endif
-
 /******************************************************************************
   Setup a signal thread with handles all signals.
   Because Linux doesn't support schemas use a mutex to check that
@@ -2002,8 +1980,8 @@ static void init_signals(void)
   DBUG_ENTER("init_signals");
 
   if (test_flags & TEST_SIGINT)
-    sigset(THR_KILL_SIGNAL,end_thread_signal);
-  sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called!
+    my_sigset(THR_KILL_SIGNAL,end_thread_signal);
+  my_sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called!
 
   if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL))
   {
@@ -2037,13 +2015,8 @@ static void init_signals(void)
   }
 #endif
   (void) sigemptyset(&set);
-#ifdef THREAD_SPECIFIC_SIGPIPE
-  sigset(SIGPIPE,abort_thread);
-  sigaddset(&set,SIGPIPE);
-#else
-  (void) signal(SIGPIPE,SIG_IGN);		// Can't know which thread
+  my_sigset(SIGPIPE,SIG_IGN);
   sigaddset(&set,SIGPIPE);
-#endif
   sigaddset(&set,SIGINT);
 #ifndef IGNORE_SIGHUP_SIGQUIT
   sigaddset(&set,SIGQUIT);
diff --git a/vio/vio.c b/vio/vio.c
index 45572b93ed6..f60a53d2f04 100644
--- a/vio/vio.c
+++ b/vio/vio.c
@@ -136,10 +136,18 @@ Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost)
 	    vio->sd);
 #if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2)
 #if !defined(NO_FCNTL_NONBLOCK)
-#if defined(__FreeBSD__)
-    fcntl(sd, F_SETFL, vio->fcntl_mode); /* Yahoo! FreeBSD patch */
-#endif
-    vio->fcntl_mode = fcntl(sd, F_GETFL);
+    /*
+      We call fcntl() to set the flags and then immediately read them back
+      to make sure that we and the system are in agreement on the state of
+      things.
+
+      An example of why we need to do this is FreeBSD (and apparently some
+      other BSD-derived systems, like Mac OS X), where the system sometimes
+      reports that the socket is set for non-blocking when it really will
+      block.
+    */
+    fcntl(sd, F_SETFL, vio->fcntl_mode);
+    vio->fcntl_mode= fcntl(sd, F_GETFL);
 #elif defined(HAVE_SYS_IOCTL_H)			/* hpux */
     /* Non blocking sockets doesn't work good on HPUX 11.0 */
     (void) ioctl(sd,FIOSNBIO,0);
diff --git a/vio/viosocket.c b/vio/viosocket.c
index 5213390e2e6..8d4c2387632 100644
--- a/vio/viosocket.c
+++ b/vio/viosocket.c
@@ -92,7 +92,14 @@ int vio_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode,
     else
       vio->fcntl_mode |= O_NONBLOCK; /* set bit */
     if (old_fcntl != vio->fcntl_mode)
-      r = fcntl(vio->sd, F_SETFL, vio->fcntl_mode);
+    {
+      r= fcntl(vio->sd, F_SETFL, vio->fcntl_mode);
+      if (r == -1)
+      {
+        DBUG_PRINT("info", ("fcntl failed, errno %d", errno));
+        vio->fcntl_mode= old_fcntl;
+      }
+    }
   }
 #else
   r= set_blocking_mode ? 0 : 1;
-- 
2.30.9