Fix user-defined signals drop by marker threads
authorHiroshi Yokota <yokota@netlab.cs.tsukuba.ac.jp>
Sat, 6 Feb 2016 01:25:48 +0000 (10:25 +0900)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 3 Mar 2016 20:43:10 +0000 (23:43 +0300)
When one user thread sets its own signal handler in multi-threaded
code, GC marker thread sometimes steal and drop user signal calls.
Because GC marker thread doesn't knows the user thread's signal
handler, and uses the default signal handler that ignores any signal
call.  This becomes to drop the signal call.

This patch inhibits to accept signals by GC marker threads except
GC system signals, and don't drop user signal calls.

This patch comes from Gauche.

* include/private/gcconfig.h (NO_MARKER_SPECIAL_SIGMASK): New macro.
* pthread_support.c (GC_start_mark_threads_inner): Unless
NO_MARKER_SPECIAL_SIGMASK, call pthread_sigmask to block all signals
(except for the ones used for GC suspend/resume if any) before
starting marker threads and restore signals mask afterwards.
* win32_threads.c (GC_start_mark_threads_inner): Likewise.

include/private/gcconfig.h
pthread_support.c
win32_threads.c

index 92d4727..cbc4aaa 100644 (file)
 # define GC_EXPLICIT_SIGNALS_UNBLOCK
 #endif
 
+#if !defined(NO_MARKER_SPECIAL_SIGMASK) \
+    && (defined(NACL) || defined(GC_WIN32_PTHREADS))
+  /* Either there is no pthread_sigmask(), or GC marker thread cannot   */
+  /* steal and drop user signal calls.                                  */
+# define NO_MARKER_SPECIAL_SIGMASK
+#endif
+
 #ifdef GC_NETBSD_THREADS
 # define SIGRTMIN 33
 # define SIGRTMAX 63
index 559c27f..8f379d2 100644 (file)
@@ -394,6 +394,9 @@ STATIC pthread_t GC_mark_threads[MAX_MARKERS];
 {
     int i;
     pthread_attr_t attr;
+#   ifndef NO_MARKER_SPECIAL_SIGMASK
+      sigset_t set, oldset;
+#   endif
 
     GC_ASSERT(I_DONT_HOLD_LOCK());
     if (available_markers_m1 <= 0) return;
@@ -424,6 +427,30 @@ STATIC pthread_t GC_mark_threads[MAX_MARKERS];
         }
       }
 #   endif /* HPUX || GC_DGUX386_THREADS */
+
+#   ifndef NO_MARKER_SPECIAL_SIGMASK
+      /* Apply special signal mask to GC marker threads, and don't drop */
+      /* user defined signals by GC marker threads.                     */
+      if (sigfillset(&set) != 0)
+        ABORT("sigfillset failed");
+
+#     if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_UTHREADS) \
+         && !defined(NACL)
+        /* These are used by GC to stop and restart the world.  */
+        if (sigdelset(&set, GC_get_suspend_signal()) != 0
+            || sigdelset(&set, GC_get_thr_restart_signal()) != 0)
+          ABORT("sigdelset failed");
+#     endif
+
+      if (pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0) {
+        WARN("pthread_sigmask set failed, no markers started,"
+             " errno = %" WARN_PRIdPTR "\n", errno);
+        GC_markers_m1 = 0;
+        (void)pthread_attr_destroy(&attr);
+        return;
+      }
+#   endif /* !NO_MARKER_SPECIAL_SIGMASK */
+
     for (i = 0; i < available_markers_m1; ++i) {
       if (0 != REAL_FUNC(pthread_create)(GC_mark_threads + i, &attr,
                               GC_mark_thread, (void *)(word)i)) {
@@ -434,6 +461,15 @@ STATIC pthread_t GC_mark_threads[MAX_MARKERS];
       }
     }
     GC_markers_m1 = i;
+
+#   ifndef NO_MARKER_SPECIAL_SIGMASK
+      /* Restore previous signal mask.  */
+      if (pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0) {
+        WARN("pthread_sigmask restore failed, errno = %" WARN_PRIdPTR "\n",
+             errno);
+      }
+#   endif
+
     (void)pthread_attr_destroy(&attr);
     GC_wait_for_markers_init();
     GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1);
index 53d0acf..614d5e8 100644 (file)
@@ -1773,6 +1773,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
       int i;
       pthread_attr_t attr;
       pthread_t new_thread;
+#     ifndef NO_MARKER_SPECIAL_SIGMASK
+        sigset_t set, oldset;
+#     endif
 
       GC_ASSERT(I_DONT_HOLD_LOCK());
       if (available_markers_m1 <= 0) return;
@@ -1786,6 +1789,20 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
       if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
         ABORT("pthread_attr_setdetachstate failed");
 
+#     ifndef NO_MARKER_SPECIAL_SIGMASK
+        /* Apply special signal mask to GC marker threads, and don't drop */
+        /* user defined signals by GC marker threads.                     */
+        if (sigfillset(&set) != 0)
+          ABORT("sigfillset failed");
+        if (pthread_sigmask(SIG_BLOCK, &set, &oldset) < 0) {
+          WARN("pthread_sigmask set failed, no markers started,"
+               " errno = %" WARN_PRIdPTR "\n", errno);
+          GC_markers_m1 = 0;
+          (void)pthread_attr_destroy(&attr);
+          return;
+        }
+#     endif /* !NO_MARKER_SPECIAL_SIGMASK */
+
       for (i = 0; i < available_markers_m1; ++i) {
         marker_last_stack_min[i] = ADDR_LIMIT;
         if (0 != pthread_create(&new_thread, &attr,
@@ -1795,6 +1812,15 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
           break;
         }
       }
+
+#     ifndef NO_MARKER_SPECIAL_SIGMASK
+        /* Restore previous signal mask.        */
+        if (pthread_sigmask(SIG_SETMASK, &oldset, NULL) < 0) {
+          WARN("pthread_sigmask restore failed, errno = %" WARN_PRIdPTR "\n",
+               errno);
+        }
+#     endif
+
       GC_markers_m1 = i;
       (void)pthread_attr_destroy(&attr);
       GC_wait_for_markers_init();