1.0.25.34: gc trigger improvements
[sbcl.git] / src / runtime / interrupt.c
index cdd7b24..d44e53c 100644 (file)
@@ -114,8 +114,13 @@ void
 sigaddset_blockable(sigset_t *sigset)
 {
     sigaddset_deferrable(sigset);
+    sigaddset_gc(sigset);
+}
+
+void
+sigaddset_gc(sigset_t *sigset)
+{
 #ifdef LISP_FEATURE_SB_THREAD
-    sigaddset(sigset,SIG_RESUME_FROM_GC);
     sigaddset(sigset,SIG_STOP_FOR_GC);
 #endif
 }
@@ -123,6 +128,7 @@ sigaddset_blockable(sigset_t *sigset)
 /* initialized in interrupt_init */
 sigset_t deferrable_sigset;
 sigset_t blockable_sigset;
+sigset_t gc_sigset;
 #endif
 
 void
@@ -163,11 +169,9 @@ void
 check_blockables_blocked_or_lose(void)
 {
 #if !defined(LISP_FEATURE_WIN32)
-    /* Get the current sigmask, by blocking the empty set. */
-    sigset_t empty,current;
+    sigset_t current;
     int i;
-    sigemptyset(&empty);
-    thread_sigmask(SIG_BLOCK, &empty, &current);
+    fill_current_sigmask(&current);
     for(i = 1; i < NSIG; i++) {
         if (sigismember(&blockable_sigset, i) && !sigismember(&current, i))
             lose("blockable signal %d not blocked\n",i);
@@ -176,16 +180,24 @@ check_blockables_blocked_or_lose(void)
 }
 
 void
-unblock_gc_signals(void)
+check_gc_signals_unblocked_in_sigset_or_lose(sigset_t *sigset)
 {
-#ifdef LISP_FEATURE_SB_THREAD
-    sigset_t new;
-    sigemptyset(&new);
-#if defined(SIG_RESUME_FROM_GC)
-    sigaddset(&new,SIG_RESUME_FROM_GC);
+#if !defined(LISP_FEATURE_WIN32)
+    int i;
+    for(i = 1; i < NSIG; i++) {
+        if (sigismember(&gc_sigset, i) && sigismember(sigset, i))
+            lose("gc signal %d blocked\n",i);
+    }
 #endif
-    sigaddset(&new,SIG_STOP_FOR_GC);
-    thread_sigmask(SIG_UNBLOCK,&new,0);
+}
+
+void
+check_gc_signals_unblocked_or_lose(void)
+{
+#if !defined(LISP_FEATURE_WIN32)
+    sigset_t current;
+    fill_current_sigmask(&current);
+    check_gc_signals_unblocked_in_sigset_or_lose(&current);
 #endif
 }
 
@@ -199,6 +211,22 @@ check_interrupts_enabled_or_lose(os_context_t *context)
         lose ("in pseudo atomic section\n");
 }
 
+/* Are we leaving WITH-GCING and already running with interrupts
+ * enabled, without the protection of *GC-INHIBIT* T and there is gc
+ * (or stop for gc) pending, but we haven't trapped yet? */
+int
+in_leaving_without_gcing_race_p(struct thread *thread)
+{
+    return ((SymbolValue(IN_WITHOUT_GCING,thread) != NIL) &&
+            (SymbolValue(INTERRUPTS_ENABLED,thread) != NIL) &&
+            (SymbolValue(GC_INHIBIT,thread) == NIL) &&
+            ((SymbolValue(GC_PENDING,thread) != NIL)
+#if defined(LISP_FEATURE_SB_THREAD)
+             || (SymbolValue(STOP_FOR_GC_PENDING,thread) != NIL)
+#endif
+             ));
+}
+
 /* Check our baroque invariants. */
 void
 check_interrupt_context_or_lose(os_context_t *context)
@@ -215,6 +243,7 @@ check_interrupt_context_or_lose(os_context_t *context)
     int gc_inhibit = (SymbolValue(GC_INHIBIT,thread) != NIL);
     int gc_pending = (SymbolValue(GC_PENDING,thread) == T);
     int pseudo_atomic_interrupted = get_pseudo_atomic_interrupted(thread);
+    int in_race_p = in_leaving_without_gcing_race_p(thread);
 #endif
     /* In the time window between leaving the *INTERRUPTS-ENABLED* NIL
      * section and trapping, a SIG_STOP_FOR_GC would see the next
@@ -224,20 +253,19 @@ check_interrupt_context_or_lose(os_context_t *context)
      * triggered, too. */
 #if 0
     if (interrupt_deferred_p)
-        if (interrupts_enabled && !pseudo_atomic_interrupted)
+        if (!(!interrupts_enabled || pseudo_atomic_interrupted || in_race_p))
             lose("Stray deferred interrupt.");
 #endif
-    /* Broken momentarily at the end of WITHOUT-GCING. */
 #if 0
     if (gc_pending)
-        if (!(pseudo_atomic_interrupted || gc_inhibit))
+        if (!(pseudo_atomic_interrupted || gc_inhibit || in_race_p))
             lose("GC_PENDING, but why?.");
 #if defined(LISP_FEATURE_SB_THREAD)
     {
         int stop_for_gc_pending =
             (SymbolValue(STOP_FOR_GC_PENDING,thread) != NIL);
         if (stop_for_gc_pending)
-            if (!(pseudo_atomic_interrupted || gc_inhibit))
+            if (!(pseudo_atomic_interrupted || gc_inhibit || in_race_p))
                 lose("STOP_FOR_GC_PENDING, but why?.");
     }
 #endif
@@ -289,6 +317,14 @@ unblock_deferrable_signals(void)
 #endif
 }
 
+void
+unblock_gc_signals(void)
+{
+#if defined(LISP_FEATURE_SB_THREAD) && !defined(LISP_FEATURE_WIN32)
+    thread_sigmask(SIG_UNBLOCK,&gc_sigset,0);
+#endif
+}
+
 \f
 /*
  * utility routines used by various signal handlers
@@ -484,6 +520,14 @@ interrupt_internal_error(os_context_t *context, boolean continuable)
         arch_skip_instruction(context);
 }
 
+boolean
+interrupt_handler_pending_p(void)
+{
+    struct thread *thread = arch_os_get_current_thread();
+    struct interrupt_data *data = thread->interrupt_data;
+    return (data->pending_handler != 0);
+}
+
 void
 interrupt_handle_pending(os_context_t *context)
 {
@@ -524,7 +568,10 @@ interrupt_handle_pending(os_context_t *context)
             sig_stop_for_gc_handler(SIG_STOP_FOR_GC,NULL,context);
         } else
 #endif
-        if (SymbolValue(GC_PENDING,thread) != NIL) {
+         /* Test for T and not for != NIL since the value :IN-PROGRESS
+          * is used in SUB-GC as part of the mechanism to supress
+          * recursive gcs.*/
+        if (SymbolValue(GC_PENDING,thread) == T) {
             /* GC_PENDING is cleared in SUB-GC, or if another thread
              * is doing a gc already we will get a SIG_STOP_FOR_GC and
              * that will clear it. */
@@ -724,8 +771,13 @@ maybe_defer_handler(void *handler, struct interrupt_data *data,
     /* If interrupts are disabled then INTERRUPT_PENDING is set and
      * not PSEDUO_ATOMIC_INTERRUPTED. This is important for a pseudo
      * atomic section inside a WITHOUT-INTERRUPTS.
+     *
+     * Also, if in_leaving_without_gcing_race_p then
+     * interrupt_handle_pending is going to be called soon, so
+     * stashing the signal away is safe.
      */
-    if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) {
+    if ((SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) ||
+        in_leaving_without_gcing_race_p(thread)) {
         store_signal_data_for_later(data,handler,signal,info,context);
         SetSymbolValue(INTERRUPT_PENDING, T,thread);
         FSHOW_SIGNAL((stderr,
@@ -860,40 +912,25 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
     SetSymbolValue(GC_PENDING,NIL,thread);
     SetSymbolValue(STOP_FOR_GC_PENDING,NIL,thread);
 
-    if(thread->state!=STATE_RUNNING) {
+    if(thread_state(thread)!=STATE_RUNNING) {
         lose("sig_stop_for_gc_handler: wrong thread state: %ld\n",
              fixnum_value(thread->state));
     }
 
-    thread->state=STATE_SUSPENDED;
+    set_thread_state(thread,STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"suspended\n"));
 
-    sigemptyset(&ss);
-    sigaddset(&ss,SIG_RESUME_FROM_GC);
-
-    /* It is possible to get SIGCONT (and probably other non-blockable
-     * signals) here. */
-    {
-        int sigret;
-        do { sigwait(&ss, &sigret); }
-        while (sigret != SIG_RESUME_FROM_GC);
-    }
-
+    wait_for_thread_state_change(thread, STATE_SUSPENDED);
     FSHOW_SIGNAL((stderr,"resumed\n"));
-    if(thread->state!=STATE_RUNNING) {
+
+    if(thread_state(thread)!=STATE_RUNNING) {
         lose("sig_stop_for_gc_handler: wrong thread state on wakeup: %ld\n",
-             fixnum_value(thread->state));
+             fixnum_value(thread_state(thread)));
     }
 
     undo_fake_foreign_function_call(context);
 }
 
-void
-sig_resume_from_gc_handler(int signal, siginfo_t *info, void *void_context)
-{
-    lose("SIG_RESUME_FROM_GC handler called.");
-}
-
 #endif
 
 void
@@ -1374,8 +1411,10 @@ interrupt_init(void)
     see_if_sigaction_nodefer_works();
     sigemptyset(&deferrable_sigset);
     sigemptyset(&blockable_sigset);
+    sigemptyset(&gc_sigset);
     sigaddset_deferrable(&deferrable_sigset);
     sigaddset_blockable(&blockable_sigset);
+    sigaddset_gc(&gc_sigset);
 
     /* Set up high level handler information. */
     for (i = 0; i < NSIG; i++) {
@@ -1470,4 +1509,3 @@ handle_trap(os_context_t *context, int trap)
         unhandled_trap_error(context);
     }
 }
-