1.0.25.32: improvements to WITHOUT-GCING
authorGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 21:49:30 +0000 (21:49 +0000)
committerGabor Melis <mega@hotpop.com>
Mon, 16 Feb 2009 21:49:30 +0000 (21:49 +0000)
- implement it with only one UNWIND-PROTECT (about 30% faster)
- add *IN-WITHOUT-GCING*
- in maybe_defer_handler defer interrupts if we are in the racy part
  of WITHOUT-GCING, that is with interrupts enabled, gc allowed but
  still *IN-WITHOUT-GCING*
- check more invariants

NEWS
src/code/cold-init.lisp
src/code/globals.lisp
src/code/sysmacs.lisp
src/compiler/generic/parms.lisp
src/runtime/interrupt.c
version.lisp-expr

diff --git a/NEWS b/NEWS
index b47062d..efefafa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ changes in sbcl-1.0.26 relative to 1.0.25:
     fault, etc. In the absence of --lose-on-corruption a warning is
     printed to stderr.
   * optimization: slightly faster gc on multithreaded builds
+  * optimization: faster WITHOUT-GCING
   * bug fix: real-time signals are not used anymore, so no more
     hanging when the system wide real-time signal queue gets full.
 
index bcde5e7..c812dab 100644 (file)
@@ -93,6 +93,7 @@
   ;; *TYPE-SYSTEM-INITIALIZED-WHEN-BOUND* so that it doesn't need to
   ;; be explicitly set in order to be meaningful.
   (setf *after-gc-hooks* nil
+        *in-without-gcing* nil
         *gc-inhibit* t
         *gc-pending* nil
         #!+sb-thread *stop-for-gc-pending* #!+sb-thread nil
index aac1ab2..96183e0 100644 (file)
@@ -22,7 +22,7 @@
                   sb!debug:*stack-top-hint*
                   *handler-clusters*
                   *restart-clusters*
-                  *gc-inhibit* *gc-pending*
+                  *in-without-gcing* *gc-inhibit* *gc-pending*
                   #!+sb-thread *stop-for-gc-pending*
                   *software-interrupt-vector* *load-verbose*
                   *load-print-stuff* *in-compilation-unit*
index 60f18d7..986e7a8 100644 (file)
     (declare (optimize (safety 0) (speed 3)))
     (sb!vm::locked-symbol-global-value-add ',symbol-name ,delta)))
 
-(defvar *gc-inhibit*) ; initialized in cold init
+;;;; these are initialized in cold init
+
+(defvar *in-without-gcing*)
+(defvar *gc-inhibit*)
 
 ;;; When the dynamic usage increases beyond this amount, the system
 ;;; notes that a garbage collection needs to occur by setting
 ;;; *GC-PENDING* to T. It starts out as NIL meaning nobody has figured
 ;;; out what it should be yet.
-(defvar *gc-pending* nil)
+(defvar *gc-pending*)
 
 #!+sb-thread
-(defvar *stop-for-gc-pending* nil)
+(defvar *stop-for-gc-pending*)
 
 (defmacro without-gcing (&body body)
   #!+sb-doc
@@ -53,24 +56,21 @@ maintained."
               ,@body))
        (if *gc-inhibit*
            (,without-gcing-body)
-           (without-interrupts
-             ;; We need to disable interrupts before disabling GC, so that
-             ;; signal handlers using locks don't accidentally try to grab
-             ;; them with GC inhibited.
-             ;;
-             ;; It would be nice to implement this with just a single UWP, but
-             ;; unfortunately it seems that it cannot be done: the naive
-             ;; solution of binding both *INTERRUPTS-ENABLED* and
-             ;; *GC-INHIBIT*, and checking for both pending GC and interrupts
-             ;; in the cleanup breaks if we have a GC pending, but no
-             ;; interrupts, and we receive an asynch unwind while checking for
-             ;; the pending GC: we unwind before handling the pending GC, and
-             ;; will be left running with further GCs blocked due to the GC
-             ;; pending flag.
+           ;; We need to disable interrupts before disabling GC, so
+           ;; that signal handlers using locks don't accidentally try
+           ;; to grab them with GC inhibited.
+           (let ((*in-without-gcing* t))
              (unwind-protect
-                  (let ((*gc-inhibit* t))
+                  (let* ((*allow-with-interrupts* nil)
+                         (*interrupts-enabled* nil)
+                         (*gc-inhibit* t))
                     (,without-gcing-body))
-               (when (or *gc-pending* #!+sb-thread *stop-for-gc-pending*)
+               ;; This is not racy becuase maybe_defer_handler
+               ;; defers signals if *GC-INHIBIT* is NIL but there
+               ;; is a pending gc or stop-for-gc.
+               (when (or *interrupt-pending*
+                         *gc-pending*
+                         #!+sb-thread *stop-for-gc-pending*)
                  (sb!unix::receive-pending-interrupt))))))))
 \f
 ;;; EOF-OR-LOSE is a useful macro that handles EOF.
index df1f733..c2691bd 100644 (file)
@@ -57,6 +57,7 @@
     sb!unix::*allow-with-interrupts*
     sb!unix::*interrupts-enabled*
     sb!unix::*interrupt-pending*
+    *in-without-gcing*
     *gc-inhibit*
     *gc-pending*
     #!-sb-thread
index 2770d4d..b63f6fb 100644 (file)
@@ -198,6 +198,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)
@@ -211,10 +227,11 @@ check_interrupt_context_or_lose(os_context_t *context)
 #if defined(LISP_FEATURE_GENCGC) && !defined(LISP_FEATURE_PPC)
 #if 0
     int interrupts_enabled = (SymbolValue(INTERRUPTS_ENABLED,thread) != NIL);
+#endif
     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);
-#endif
+    int in_race_p = in_leaving_without_gcing_race_p(thread);
     /* In the time window between leaving the *INTERRUPTS-ENABLED* NIL
      * section and trapping, a SIG_STOP_FOR_GC would see the next
      * check fail, for this reason sig_stop_for_gc handler does not
@@ -223,20 +240,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
@@ -723,8 +739,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,
index 3395220..71d5467 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.25.31"
+"1.0.25.32"