X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fgc.lisp;h=8a8fe4612fcb0261f9f2ffabf26d5bf12144984d;hb=a160917364f85b38dc0826a5e3dcef87e3c4c62c;hp=fbd4e4bed87d84c758e0ece56a500e48c795ab06;hpb=bfb7c2d573bacfd9c5f3f243b7c1589f81f11406;p=sbcl.git diff --git a/src/code/gc.lisp b/src/code/gc.lisp index fbd4e4b..8a8fe46 100644 --- a/src/code/gc.lisp +++ b/src/code/gc.lisp @@ -118,13 +118,14 @@ (declaim (type unsigned-byte *n-bytes-freed-or-purified*)) (defvar *n-bytes-freed-or-purified* 0) (defun gc-reinit () - (gc-on) + (setq *gc-inhibit* nil) (gc) (setf *n-bytes-freed-or-purified* 0 + *gc-run-time* 0 ;; See comment in interr.lisp *heap-exhausted-error-condition* (make-condition 'heap-exhausted-error))) -(declaim (ftype (function () unsigned-byte) get-bytes-consed)) +(declaim (ftype (sfunction () unsigned-byte) get-bytes-consed)) (defun get-bytes-consed () #!+sb-doc "Return the number of bytes consed since the program began. Typically @@ -176,60 +177,105 @@ run in any thread.") ;;; For GENCGC all generations < GEN will be GC'ed. -(defvar *already-in-gc* - (sb!thread:make-mutex :name "GC lock") "ID of thread running SUB-GC") +(defvar *already-in-gc* (sb!thread:make-mutex :name "GC lock")) + +;;; A unique GC id. This is supplied for code that needs to detect +;;; whether a GC has happened since some earlier point in time. For +;;; example: +;;; +;;; (let ((epoch *gc-epoch*)) +;;; ... +;;; (unless (eql epoch *gc-epoch) +;;; ....)) +;;; +;;; This isn't just a fixnum counter since then we'd have theoretical +;;; problems when exactly 2^29 GCs happen between epoch +;;; comparisons. Unlikely, but the cost of using a cons instead is too +;;; small to measure. -- JES, 2007-09-30 +(declaim (type cons *gc-epoch*)) +(defvar *gc-epoch* (cons nil nil)) (defun sub-gc (&key (gen 0)) - (unless (eq sb!thread:*current-thread* - (sb!thread::mutex-value *already-in-gc*)) - ;; With gencgc, unless *GC-PENDING* every allocation in this - ;; function triggers another gc, potentially exceeding maximum - ;; interrupt nesting. - (setq *gc-pending* t) - (unless *gc-inhibit* - (sb!thread:with-mutex (*already-in-gc*) - (let ((old-usage (dynamic-usage)) - (new-usage 0)) - (unsafe-clear-roots) - ;; We need to disable interrupts for GC, but we also want - ;; to run as little as possible without them. - (without-interrupts - (gc-stop-the-world) - (let ((start-time (get-internal-run-time))) - (collect-garbage gen) - (incf *gc-run-time* - (- (get-internal-run-time) start-time))) - (setf *gc-pending* nil - new-usage (dynamic-usage)) - (gc-start-the-world)) - ;; Interrupts re-enabled, but still inside the mutex. - ;; In a multithreaded environment the other threads will - ;; see *n-b-f-o-p* change a little late, but that's OK. - (let ((freed (- old-usage new-usage))) - ;; GENCGC occasionally reports negative here, but the - ;; current belief is that it is part of the normal order - ;; of things and not a bug. - (when (plusp freed) - (incf *n-bytes-freed-or-purified* freed))))) - ;; Outside the mutex, these may cause another GC. FIXME: it can - ;; potentially exceed maximum interrupt nesting by triggering - ;; GCs. - ;; - ;; Can that be avoided by having the finalizers and hooks run only - ;; from the outermost SUB-GC? - ;; - ;; KLUDGE: Don't run the hooks in GC's triggered by dying threads, - ;; so that user-code never runs with - ;; (thread-alive-p *current-thread*) => nil - ;; The long-term solution will be to keep a separate thread for - ;; finalizers and after-gc hooks. - (when (sb!thread:thread-alive-p sb!thread:*current-thread*) + (cond (*gc-inhibit* + (setf *gc-pending* t) + nil) + (t + (without-interrupts + (setf *gc-pending* :in-progress) + ;; Tricks to to prevent triggerring a recursive gc. This is + ;; like a WITHOUT-GCING inside the lock except that we + ;; cannot call MAYBE-HANDLE-PENDING-GC at the end, because + ;; that would lead to a recursive attempt on the lock. In + ;; case you are wondering, wrapping the lock in a + ;; WITHOUT-GCING would also deadlock. The + ;; *IN-WITHOUT-GCING* part is used to tell the runtime that + ;; it's ok to have a pending gc even though *GC-INHIBIT* is + ;; NIL. + ;; + ;; Now, if GET-MUTEX did not cons, that would be enough. + ;; Because it does, we need the :IN-PROGRESS bit above to + ;; tell the runtime not to trigger gcs. + (let ((sb!impl::*in-without-gcing* t) + (sb!impl::*deadline* nil) + (sb!impl::*deadline-seconds* nil)) + (sb!thread:with-mutex (*already-in-gc*) + (let ((*gc-inhibit* t)) + (let ((old-usage (dynamic-usage)) + (new-usage 0)) + (unsafe-clear-roots) + (gc-stop-the-world) + (let ((start-time (get-internal-run-time))) + (collect-garbage gen) + (setf *gc-epoch* (cons nil nil)) + (incf *gc-run-time* + (- (get-internal-run-time) start-time))) + (setf *gc-pending* nil + new-usage (dynamic-usage)) + #!+sb-thread + (assert (not *stop-for-gc-pending*)) + (gc-start-the-world) + ;; In a multithreaded environment the other threads + ;; will see *n-b-f-o-p* change a little late, but + ;; that's OK. + (let ((freed (- old-usage new-usage))) + ;; GENCGC occasionally reports negative here, but + ;; the current belief is that it is part of the + ;; normal order of things and not a bug. + (when (plusp freed) + (incf *n-bytes-freed-or-purified* freed))))))) + ;; While holding the mutex we were protected from + ;; SIG_STOP_FOR_GC and recursive GCs. Now, in order to + ;; preserve the invariant (*GC-PENDING* -> + ;; pseudo-atomic-interrupted or *GC-INHIBIT*), let's check + ;; explicitly for a pending gc before interrupts are + ;; enabled again. + (maybe-handle-pending-gc)) + t))) + +(defun post-gc () + ;; Outside the mutex, interrupts may be enabled: these may cause + ;; another GC. FIXME: it can potentially exceed maximum interrupt + ;; nesting by triggering GCs. + ;; + ;; Can that be avoided by having the finalizers and hooks run only + ;; from the outermost SUB-GC? If the nested GCs happen in interrupt + ;; handlers that's not enough. + ;; + ;; KLUDGE: Don't run the hooks in GC's if: + ;; + ;; A) this thread is dying, so that user-code never runs with + ;; (thread-alive-p *current-thread*) => nil + ;; + ;; B) interrupts are disabled somewhere up the call chain since we + ;; don't want to run user code in such a case. + ;; + ;; The long-term solution will be to keep a separate thread for + ;; finalizers and after-gc hooks. + (when (sb!thread:thread-alive-p sb!thread:*current-thread*) + (when *allow-with-interrupts* + (with-interrupts (run-pending-finalizers) - (dolist (hook *after-gc-hooks*) - (handler-case - (funcall hook) - (error (c) - (warn "Error calling after-GC hook ~S:~% ~A" hook c)))))))) + (call-hooks "after-GC" *after-gc-hooks* :on-error :warn))))) ;;; This is the user-advertised garbage collection function. (defun gc (&key (gen 0) (full nil) &allow-other-keys) @@ -239,13 +285,17 @@ run in any thread.") #!+(and sb-doc (not gencgc)) "Initiate a garbage collection. GEN may be provided for compatibility with generational garbage collectors, but is ignored in this implementation." - (sub-gc :gen (if full 6 gen))) + (when (sub-gc :gen (if full 6 gen)) + (post-gc))) (defun unsafe-clear-roots () ;; KLUDGE: Do things in an attempt to get rid of extra roots. Unsafe ;; as having these cons more then we have space left leads to huge ;; badness. (scrub-control-stack) + ;; Power cache of the bignum printer: drops overly large bignums and + ;; removes duplicate entries. + (scrub-power-cache) ;; FIXME: CTYPE-OF-CACHE-CLEAR isn't thread-safe. #!-sb-thread (ctype-of-cache-clear)) @@ -272,18 +322,3 @@ run in any thread.") (or #!+sb-thread *stop-for-gc-pending* *gc-pending*)) (sb!unix::receive-pending-interrupt))) - -;;; These work both regardless of whether we're inside WITHOUT-GCING -;;; or not. -(defun gc-on () - #!+sb-doc - "Enable the garbage collector." - (setq *gc-inhibit* nil) - (maybe-handle-pending-gc) - nil) - -(defun gc-off () - #!+sb-doc - "Disable the garbage collector." - (setq *gc-inhibit* t) - nil)