Add register-gc-event, deregister-gc-event, registered-gc-event.
authorMatt Birkholz <puck@birchwood-abbey.net>
Tue, 18 Aug 2015 00:19:15 +0000 (17:19 -0700)
committerMatt Birkholz <puck@birchwood-abbey.net>
Thu, 26 Nov 2015 07:59:18 +0000 (00:59 -0700)
Punt the hook/record-statistic! fluid.  With-gc-notification now uses
dynamic-wind to register and deregister a GC thread event for the
current thread.

Do not use ANY fluid state (e.g. nearest-cmdl/port) during a GC.  Use
the console-i/o-port in hook/gc-start and hook/gc-finish.  GCs can
happen in the thread system when there is no current thread.

The fluid state IS defined during the GC thread events.  At the start
of such events, signal a REPL abort if the heap is low.

src/runtime/gc.scm
src/runtime/gcnote.scm
src/runtime/gcstat.scm
src/runtime/make.scm
src/runtime/runtime.pkg
src/runtime/thread.scm

index e749b08ae4b80f747afb84b50568eb1397715094..b050e9f6b8d00afd140736867c26c6915f3482d9 100644 (file)
@@ -117,6 +117,10 @@ USA.
   #f)
 
 (define (gc-finish start-value space-remaining)
+  (hook/gc-finish start-value space-remaining)
+  ((ucode-primitive request-interrupts! 1) interrupt-bit/after-gc))
+
+(define (abort-if-heap-low space-remaining)
   (if (< space-remaining 4096)
       (if gc-boot-loading?
          (let ((console ((ucode-primitive tty-output-channel 0))))
@@ -133,9 +137,7 @@ USA.
            (cmdl-message/active
             (lambda (port)
               port
-              (with-gc-notification! #t gc-clean)))))))
-  ((ucode-primitive request-interrupts! 1) interrupt-bit/after-gc)
-  (hook/gc-finish start-value space-remaining))
+              (with-gc-notification! #t gc-clean))))))))
 
 (define gc-boot-loading?)
 
index e5139059db89c59617db2182435282c5cb2723f3..1bacfd52653d22e12b0e510015ef242fabc56914 100644 (file)
@@ -29,30 +29,109 @@ USA.
 
 (declare (usual-integrations))
 \f
+(define (initialize-package!)
+  (add-gc-daemon! signal-gc-events))
+
 (define (toggle-gc-notification!)
-  (set-fluid! hook/record-statistic!
-             (let ((current (fluid hook/record-statistic!)))
-               (cond ((eq? current gc-notification) default/record-statistic!)
-                     ((eq? current default/record-statistic!) gc-notification)
-                     (else (error "Can't grab GC statistics hook")))))
+  (if (registered-gc-event)
+      (deregister-gc-event)
+      (register-gc-event gc-notification))
   unspecific)
 
 (define (set-gc-notification! #!optional on?)
   (let ((on? (if (default-object? on?) #T on?)))
-    (set-fluid! hook/record-statistic!
-               (let ((current (fluid hook/record-statistic!)))
-                 (if (or (eq? current gc-notification)
-                         (eq? current default/record-statistic!))
-                     (if on?
-                         gc-notification
-                         default/record-statistic!)
-                     (error "Can't grab GC statistics hook"))))
+    (if on?
+       (register-gc-event gc-notification)
+       (deregister-gc-event))
     unspecific))
 
 (define (with-gc-notification! notify? thunk)
-  (let-fluid hook/record-statistic!
-            (if notify? gc-notification default/record-statistic!)
-    thunk))
+  (let ((outside))
+    (dynamic-wind
+     (lambda ()
+       (set! outside (registered-gc-event))
+       (if notify?
+          (register-gc-event gc-notification)
+          (deregister-gc-event)))
+     thunk
+     (lambda ()
+       (if outside
+          (register-gc-event outside)
+          (deregister-gc-event))
+       (set! outside)))))
+\f
+;;;; GC Events
+
+(define gc-events '())                 ;Weak alist of threads X events.
+(define gc-events-mutex (make-thread-mutex))
+
+(define (register-gc-event event)
+  (guarantee-procedure-of-arity event 1 'register-gc-event)
+  (with-thread-mutex-lock gc-events-mutex
+    (lambda ()
+      (clean-gc-events)
+      (let* ((thread (current-thread))
+            (entry (weak-assq thread gc-events)))
+       (if entry
+           (weak-set-cdr! entry event)
+           (set! gc-events (cons (weak-cons thread event) gc-events)))))))
+
+(define (deregister-gc-event)
+  (with-thread-mutex-lock gc-events-mutex
+    (lambda ()
+      (clean-gc-events)
+      (let* ((thread (current-thread))
+            (entry (weak-assq thread gc-events)))
+       (if entry
+           (set! gc-events (delq! entry gc-events)))))))
+
+(define (%deregister-gc-event thread)
+  ;; This procedure is called by the thread system when a thread exits
+  ;; or calls deregister-all-events.  It may interrupt the procedures
+  ;; above, but it does not modify the gc-events list.  Fortunately a
+  ;; thread cannot race itself to both set and clear its entry.
+  (let ((entry (weak-assq thread gc-events)))
+    (if entry
+       (weak-set-cdr! entry #f))))
+
+(define (clean-gc-events)
+  (set! gc-events
+       (filter! (lambda (weak)
+                  (let ((thread (weak-car weak)))
+                    (and thread
+                         (weak-cdr weak) ;not cleared by %deregister...
+                         (not (eq? 'DEAD (thread-execution-state thread))))))
+                gc-events)))
+
+(define (registered-gc-event)
+  (let ((entry (weak-assq (current-thread) gc-events)))
+    (and entry (weak-cdr entry))))
+
+(define (signal-gc-events)
+  (for-each
+    (lambda (entry)
+      (let ((thread (weak-car entry))
+           (event (weak-cdr entry)))
+       (if (and thread event)
+           (signal-thread-event
+               thread
+             (named-lambda (gc-event)
+               (abort-if-heap-low (gc-statistic/heap-left last-statistic))
+               (event last-statistic))
+             #t))))
+    gc-events))
+
+(define (weak-assq obj alist)
+  (let loop ((alist alist))
+    (if (pair? alist)
+       (let* ((entry (car alist))
+              (key (weak-car entry)))
+         (if (eq? key obj)
+             entry
+             (loop (cdr alist))))
+       #f)))
+\f
+;;;; Output
 
 (define (gc-notification statistic)
   (print-statistic statistic (notification-output-port)))
index 1d74cc8528752190f124cbf5a4ef2f8b91eed3b9..4aa6268dee24bbe940367991e85ba134aee02c7c 100644 (file)
@@ -30,7 +30,6 @@ USA.
 (declare (usual-integrations))
 
 (define (initialize-package!)
-  (set! hook/record-statistic! (make-fluid default/record-statistic!))
   (set! history-modes
        `((NONE . ,none:install-history!)
          (BOUNDED . ,bounded:install-history!)
@@ -44,7 +43,7 @@ USA.
   unspecific)
 
 (define (recorder/gc-start)
-  (port/gc-start (nearest-cmdl/port))
+  (port/gc-start console-i/o-port)
   (set! this-gc-start-clock (real-time-clock))
   (set! this-gc-start (process-time-clock))
   unspecific)
@@ -57,7 +56,7 @@ USA.
     (statistics-flip this-gc-start end-time
                     space-remaining
                     this-gc-start-clock end-time-clock))
-  (port/gc-finish (nearest-cmdl/port)))
+  (port/gc-finish console-i/o-port))
 \f
 (define timestamp)
 (define total-gc-time)
@@ -105,18 +104,11 @@ USA.
     (set! last-gc-end end-time)
     (set! last-gc-start-clock start-clock)
     (set! last-gc-end-clock end-clock)
-    (record-statistic! statistic)
-    ((fluid hook/record-statistic!) statistic)))
+    (record-statistic! statistic)))
 
 (define (gc-statistic/meter stat)
   (car (gc-statistic/timestamp stat)))
 
-(define hook/record-statistic!)
-
-(define (default/record-statistic! statistic)
-  statistic
-  false)
-
 (define (gctime)
   (internal-time/ticks->seconds total-gc-time))
 \f
index 7e4b7c20e57adcee6021d10810323d1d16aaa774..20e3be250cf73ae7bbd3b12f8bd9367fcc3d86ab 100644 (file)
@@ -518,6 +518,7 @@ USA.
    ;; REP Loops
    (RUNTIME INTERRUPT-HANDLER)
    (RUNTIME GC-STATISTICS)
+   (RUNTIME GC-NOTIFICATION)
    (RUNTIME REP)
    ;; Debugging
    (RUNTIME COMPILER-INFO)
index 3987da58ee547f2d8e3070e12fd9e0c9f9be65a3..1798fd8730a95f3835ee8e584ce71dfcc97eac2a 100644 (file)
@@ -1990,6 +1990,8 @@ USA.
          hook/gc-start)
   (export (runtime error-handler)
          hook/hardware-trap)
+  (export (runtime gc-notification)
+         abort-if-heap-low)
   (initialization (initialize-package!)))
 
 (define-package (runtime gc-daemons)
@@ -2024,11 +2026,17 @@ USA.
   (files "gcnote")
   (parent (runtime))
   (export ()
+         deregister-gc-event
          gc-statistic->string
          print-gc-statistics
+         register-gc-event
+         registered-gc-event
          set-gc-notification!
          toggle-gc-notification!
-         with-gc-notification!))
+         with-gc-notification!)
+  (export (runtime thread)
+         %deregister-gc-event)
+  (initialization (initialize-package!)))
 
 (define-package (runtime gc-statistics)
   (files "gcstat")
@@ -2049,8 +2057,7 @@ USA.
          gc-timestamp
          gctime)
   (export (runtime gc-notification)
-         default/record-statistic!
-         hook/record-statistic!)
+         last-statistic)
   (initialization (initialize-package!)))
 
 (define-package (runtime generic-i/o-port)
index a5af9682bb1ab67cdda4ce4df9a2f788f6d7a4d1..44b37dc28e8629995af6e536f73abe13de12d7d5 100644 (file)
@@ -858,9 +858,11 @@ USA.
           (set-thread/block-events?! thread block?)))
      unspecific)))
 \f
-(define (signal-thread-event thread event)
+(define (signal-thread-event thread event #!optional no-error?)
   (guarantee-thread thread 'SIGNAL-THREAD-EVENT)
-  (let ((self first-running-thread))
+  (let ((self first-running-thread)
+       (noerr? (and (not (default-object? no-error?))
+                    no-error?)))
     (if (eq? thread self)
        (let ((block-events? (block-thread-events)))
          (%add-pending-event thread event)
@@ -869,12 +871,14 @@ USA.
        (without-interrupts
         (lambda ()
           (if (eq? 'DEAD (thread/execution-state thread))
-              (signal-thread-dead thread "signal event to"
-                                  signal-thread-event thread event))
-          (%signal-thread-event thread event)
-          (if (and (not self) first-running-thread)
-              (run-thread first-running-thread)
-              (%maybe-toggle-thread-timer)))))))
+              (if (not noerr?)
+                  (signal-thread-dead thread "signal event to"
+                                      signal-thread-event thread event))
+              (begin
+                (%signal-thread-event thread event)
+                (if (and (not self) first-running-thread)
+                    (run-thread first-running-thread)
+                    (%maybe-toggle-thread-timer)))))))))
 
 (define (%signal-thread-event thread event)
   (%add-pending-event thread event)