+(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))
+ (cond (*gc-inhibit*
+ (setf *gc-pending* t)
+ nil)
+ (t
+ (flet ((perform-gc ()
+ ;; Called from WITHOUT-GCING and WITHOUT-INTERRUPTS
+ ;; after the world has been stopped, but it's an
+ ;; awkwardly long piece of code to nest so deeply.
+ (let ((old-usage (dynamic-usage))
+ (new-usage 0)
+ (start-time (get-internal-run-time)))
+ (collect-garbage gen)
+ (setf *gc-epoch* (cons nil nil))
+ (let ((run-time (- (get-internal-run-time) start-time)))
+ ;; KLUDGE: Sometimes we see the second getrusage() call
+ ;; return a smaller value than the first, which can
+ ;; lead to *GC-RUN-TIME* to going negative, which in
+ ;; turn is a type-error.
+ (when (plusp run-time)
+ (incf *gc-run-time* run-time)))
+ #!+sb-safepoint
+ (setf *stop-for-gc-pending* nil)
+ (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.
+ ;; N.B. the outer without-gcing prevents this
+ ;; function from being entered, so no need for
+ ;; locking.
+ (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))))))
+ (declare (inline perform-gc))
+ ;; Let's make sure we're not interrupted and that none of
+ ;; the deadline or deadlock detection stuff triggers.
+ (without-interrupts
+ (sb!thread::without-thread-waiting-for
+ (:already-without-interrupts t)
+ (let ((sb!impl::*deadline* nil)
+ (sb!impl::*deadline-seconds* nil)
+ (epoch *gc-epoch*))
+ (loop
+ ;; GCing must be done without-gcing to avoid
+ ;; recursive GC... but we can't block on
+ ;; *already-in-gc* inside without-gcing: that would
+ ;; cause a deadlock.
+ (without-gcing
+ ;; Try to grab that mutex. On acquisition, stop
+ ;; the world from with the mutex held, and then
+ ;; execute the remainder of the GC: stopping the
+ ;; world with interrupts disabled is the mother of
+ ;; all critical sections.
+ (cond ((sb!thread:with-mutex (*already-in-gc* :wait-p nil)
+ (unsafe-clear-roots gen)
+ (gc-stop-the-world)
+ t)
+ ;; Success! GC.
+ (perform-gc)
+ ;; Return, but leave *gc-pending* as is: we
+ ;; did allocate a tiny bit after GCing. In
+ ;; theory, this could lead to a long chain
+ ;; of tail-recursive (but not in explicit
+ ;; tail position) GCs, but that doesn't
+ ;; seem likely to happen too often... And
+ ;; the old code already suffered from this
+ ;; problem.
+ (return t))
+ (t
+ ;; Some other thread is trying to GC. Clear
+ ;; *gc-pending* (we already know we want a
+ ;; GC to happen) and either let
+ ;; without-gcing figure out that the world
+ ;; is stopping, or try again.
+ (setf *gc-pending* nil))))
+ ;; we just wanted a minor GC, and a GC has
+ ;; occurred. Leave, but don't execute after-gc
+ ;; hooks.
+ ;;
+ ;; Return a 0 for easy ternary logic in the C
+ ;; runtime.
+ (when (and (eql gen 0)
+ (neq epoch *gc-pending*))
+ (return 0))))))))))
+
+(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*
+ (sb!thread::without-thread-waiting-for ()
+ (with-interrupts
+ (run-pending-finalizers)
+ (call-hooks "after-GC" *after-gc-hooks* :on-error :warn))))))