]> granicus.if.org Git - gc/commitdiff
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 92d4727784fcf29cac247db076713890046d5c70..cbc4aaafbee937a32525f0a0b05312e0c31792ef 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 559c27fcb7eba44d70d882d02dbd94606e66122a..8f379d2dca4694121b9106c844b80159e7f01c9b 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 53d0acfdf732b2a7700d74ae16ada86ae91dc201..614d5e87eb6e445626723ace7793ff711066b7dd 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();