X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fgc.lisp;h=96ef1d94696369462c3844e7db39a29060e35d29;hb=408ed62925d643c163f0e9fc7b3fd41cce65fbea;hp=30cd93da5bc91661d29d72e2d86500c6de781454;hpb=934b35cf683bacd5c15842a5012f852589ae2314;p=sbcl.git diff --git a/src/code/gc.lisp b/src/code/gc.lisp index 30cd93d..96ef1d9 100644 --- a/src/code/gc.lisp +++ b/src/code/gc.lisp @@ -13,51 +13,44 @@ ;;;; DYNAMIC-USAGE and friends -(declaim (special sb!vm:*read-only-space-free-pointer* - sb!vm:*static-space-free-pointer*)) - -(eval-when (:compile-toplevel :execute) - (sb!xc:defmacro def-c-var-frob (lisp-fun c-var-name) - `(progn - #!-sb-fluid (declaim (inline ,lisp-fun)) - (defun ,lisp-fun () - (sb!alien:extern-alien ,c-var-name (sb!alien:unsigned 32)))))) - +#!-sb-fluid +(declaim (inline current-dynamic-space-start)) +#!+gencgc +(defun current-dynamic-space-start () sb!vm:dynamic-space-start) #!-gencgc -(progn - ;; This is called once per PROFILEd function call, so it's worth a - ;; little possible space cost to reduce its time cost. - #!-sb-fluid - (declaim (inline current-dynamic-space-start)) - (def-c-var-frob current-dynamic-space-start "current_dynamic_space")) +(defun current-dynamic-space-start () + (sb!alien:extern-alien "current_dynamic_space" sb!alien:unsigned-long)) #!-sb-fluid -(declaim (inline dynamic-usage)) ; to reduce PROFILEd call overhead -#!+(or cgc gencgc) -(def-c-var-frob dynamic-usage "bytes_allocated") -#!-(or cgc gencgc) +(declaim (inline dynamic-usage)) +#!+gencgc +(defun dynamic-usage () + (sb!alien:extern-alien "bytes_allocated" sb!alien:unsigned-long)) +#!-gencgc (defun dynamic-usage () (the (unsigned-byte 32) (- (sb!sys:sap-int (sb!c::dynamic-space-free-pointer)) (current-dynamic-space-start)))) (defun static-space-usage () - (- (* sb!vm:*static-space-free-pointer* sb!vm:word-bytes) + (- (ash sb!vm:*static-space-free-pointer* sb!vm:n-fixnum-tag-bits) sb!vm:static-space-start)) (defun read-only-space-usage () - (- (* sb!vm::*read-only-space-free-pointer* sb!vm:word-bytes) + (- (ash sb!vm::*read-only-space-free-pointer* sb!vm:n-fixnum-tag-bits) sb!vm:read-only-space-start)) (defun control-stack-usage () - #!-x86 (- (sb!sys:sap-int (sb!c::control-stack-pointer-sap)) - sb!vm:control-stack-start) - #!+x86 (- sb!vm:control-stack-end - (sb!sys:sap-int (sb!c::control-stack-pointer-sap)))) + #!-stack-grows-downward-not-upward + (- (sb!sys:sap-int (sb!c::control-stack-pointer-sap)) + (sb!sys:sap-int (sb!di::descriptor-sap sb!vm:*control-stack-start*))) + #!+stack-grows-downward-not-upward + (- (sb!sys:sap-int (sb!di::descriptor-sap sb!vm:*control-stack-end*)) + (sb!sys:sap-int (sb!c::control-stack-pointer-sap)))) (defun binding-stack-usage () (- (sb!sys:sap-int (sb!c::binding-stack-pointer-sap)) - sb!vm:binding-stack-start)) + (sb!sys:sap-int (sb!di::descriptor-sap sb!vm:*binding-stack-start*)))) ;;;; ROOM @@ -67,21 +60,28 @@ (format t "Static space usage is: ~10:D bytes.~%" (static-space-usage)) (format t "Control stack usage is: ~10:D bytes.~%" (control-stack-usage)) (format t "Binding stack usage is: ~10:D bytes.~%" (binding-stack-usage)) + #!+sb-thread + (format t + "Control and binding stack usage is for the current thread only.~%") (format t "Garbage collection is currently ~:[enabled~;DISABLED~].~%" - *gc-inhibit*)) + *gc-inhibit*)) (defun room-intermediate-info () (room-minimal-info) (sb!vm:memory-usage :count-spaces '(:dynamic) - :print-spaces t - :cutoff 0.05s0 - :print-summary nil)) + :print-spaces t + :cutoff 0.05f0 + :print-summary nil)) (defun room-maximal-info () - (room-minimal-info) - (sb!vm:memory-usage :count-spaces '(:static :dynamic)) - (sb!vm:instance-usage :dynamic :top-n 10) - (sb!vm:instance-usage :static :top-n 10)) + ;; FIXME: SB!VM:INSTANCE-USAGE calls suppressed until bug 344 is fixed + (room-intermediate-info) + ;; old way, could be restored when bug 344 fixed: + ;;x (room-minimal-info) + ;;x (sb!vm:memory-usage :count-spaces '(:static :dynamic)) + ;;x (sb!vm:instance-usage :dynamic :top-n 10) + ;;x (sb!vm:instance-usage :static :top-n 10) + ) (defun room (&optional (verbosity :default)) #!+sb-doc @@ -111,17 +111,15 @@ ;;; allocated and never freed.) (declaim (type unsigned-byte *n-bytes-freed-or-purified*)) (defvar *n-bytes-freed-or-purified* 0) -(push (lambda () - (setf *n-bytes-freed-or-purified* 0)) - ;; KLUDGE: It's probably not quite safely right either to do - ;; this in *BEFORE-SAVE-INITIALIZATIONS* (since consing, or even - ;; worse, something which depended on (GET-BYTES-CONSED), might - ;; happen after that) or in *AFTER-SAVE-INITIALIZATIONS*. But - ;; it's probably not a big problem, and there seems to be no - ;; other obvious time to do it. -- WHN 2001-07-30 - *after-save-initializations*) - -(declaim (ftype (function () unsigned-byte) get-bytes-consed)) +(defun gc-reinit () + (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 (sfunction () unsigned-byte) get-bytes-consed)) (defun get-bytes-consed () #!+sb-doc "Return the number of bytes consed since the program began. Typically @@ -133,260 +131,176 @@ and submit it as a patch." (+ (dynamic-usage) *n-bytes-freed-or-purified*)) -;;;; variables and constants - -;;; the minimum amount of dynamic space which must be consed before a -;;; GC will be triggered -;;; -;;; Unlike CMU CL, we don't export this variable. (There's no need to, -;;; since our BYTES-CONSED-BETWEEN-GCS function is SETFable.) -(defvar *bytes-consed-between-gcs* (* 4 (expt 10 6))) -(declaim (type index *bytes-consed-between-gcs*)) - ;;;; GC hooks -(defvar *before-gc-hooks* nil ; actually initialized in cold init - #!+sb-doc - "A list of functions that are called before garbage collection occurs. - The functions should take no arguments.") - -(defvar *after-gc-hooks* nil ; actually initialized in cold init - #!+sb-doc - "A list of functions that are called after garbage collection occurs. - The functions should take no arguments.") - -(defvar *gc-notify-stream* nil ; (actually initialized in cold init) - #!+sb-doc - "When non-NIL, this must be a STREAM; and the functions bound to - *GC-NOTIFY-BEFORE* and *GC-NOTIFY-AFTER* are called with the - STREAM value before and after a garbage collection occurs - respectively.") +(defvar *after-gc-hooks* nil + "Called after each garbage collection, except for garbage collections +triggered during thread exits. In a multithreaded environment these hooks may +run in any thread.") -(defvar *gc-run-time* 0 - #!+sb-doc - "the total CPU time spent doing garbage collection (as reported by - GET-INTERNAL-RUN-TIME)") -(declaim (type index *gc-run-time*)) - -;;; a limit to help catch programs which allocate too much memory, -;;; since a hard heap overflow is so hard to recover from -(declaim (type (or unsigned-byte null) *soft-heap-limit*)) -(defvar *soft-heap-limit* nil) - -;;; When the dynamic usage increases beyond this amount, the system -;;; notes that a garbage collection needs to occur by setting -;;; *NEED-TO-COLLECT-GARBAGE* to T. It starts out as NIL meaning -;;; nobody has figured out what it should be yet. -(defvar *gc-trigger* nil) - -(declaim (type (or index null) *gc-trigger*)) - -;;; On the X86, we store the GC trigger in a ``static'' symbol instead -;;; of letting magic C code handle it. It gets initialized by the -;;; startup code. -#!+x86 -(defvar sb!vm::*internal-gc-trigger*) - -;;;; The following specials are used to control when garbage collection -;;;; occurs. - -;;; When non-NIL, inhibits garbage collection. -(defvar *gc-inhibit*) ; initialized in cold init - -;;; This flag is used to prevent recursive entry into the garbage -;;; collector. -(defvar *already-maybe-gcing*) ; initialized in cold init - -;;; When T, indicates that the dynamic usage has exceeded the value -;;; *GC-TRIGGER*. -(defvar *need-to-collect-garbage* nil) ; initialized in cold init - -(defun default-gc-notify-before (notify-stream bytes-in-use) - (declare (type stream notify-stream)) - (format notify-stream - "~&; GC is beginning with ~:D bytes in use at internal runtime ~:D.~%" - bytes-in-use - (get-internal-run-time)) - (finish-output notify-stream)) -(defparameter *gc-notify-before* #'default-gc-notify-before - #!+sb-doc - "This function bound to this variable is invoked before GC'ing (unless - *GC-NOTIFY-STREAM* is NIL) with the value of *GC-NOTIFY-STREAM* and - current amount of dynamic usage (in bytes). It should notify the - user that the system is going to GC.") - -(defun default-gc-notify-after (notify-stream - bytes-retained - bytes-freed - new-trigger) - (declare (type stream notify-stream)) - (format notify-stream - "~&; GC has finished with ~:D bytes in use (~:D bytes freed)~@ - ; at internal runtime ~:D. The new GC trigger is ~:D bytes.~%" - bytes-retained - bytes-freed - (get-internal-run-time) - new-trigger) - (finish-output notify-stream)) -(defparameter *gc-notify-after* #'default-gc-notify-after - #!+sb-doc - "The function bound to this variable is invoked after GC'ing with -the value of *GC-NOTIFY-STREAM*, the amount of dynamic usage (in -bytes) now free, the number of bytes freed by the GC, and the new GC -trigger threshold. The function should notify the user that the system -has finished GC'ing.") ;;;; internal GC -(sb!alien:def-alien-routine collect-garbage sb!c-call:int - #!+gencgc (last-gen sb!c-call:int)) - -(sb!alien:def-alien-routine set-auto-gc-trigger sb!c-call:void - (dynamic-usage sb!c-call:unsigned-long)) +(sb!alien:define-alien-routine collect-garbage sb!alien:int + (#!+gencgc last-gen #!-gencgc ignore sb!alien:int)) -(sb!alien:def-alien-routine clear-auto-gc-trigger sb!c-call:void) +#!+sb-thread +(progn + (sb!alien:define-alien-routine gc-stop-the-world sb!alien:void) + (sb!alien:define-alien-routine gc-start-the-world sb!alien:void)) +#!-sb-thread +(progn + (defun gc-stop-the-world ()) + (defun gc-start-the-world ())) -;;; This variable contains the function that does the real GC. This is -;;; for low-level GC experimentation. Do not touch it if you do not -;;; know what you are doing. -(defvar *internal-gc* #'collect-garbage) +#!+gencgc +(progn + (sb!alien:define-alien-variable ("gc_logfile" %gc-logfile) (* char)) + (defun (setf gc-logfile) (pathname) + "Use PATHNAME to log garbage collections. If non-null, the +designated file is opened before and after each collection, and +generation statistics are appended to it. To stop writing the log, use +NIL as the pathname." + (let ((new (when pathname + (sb!alien:make-alien-string + (native-namestring (translate-logical-pathname pathname) + :as-file t)))) + (old %gc-logfile)) + (setf %gc-logfile new) + (when old + (sb!alien:free-alien old)))) + (defun gc-logfile () + "Return the name of the current GC logfile." + (let ((val %gc-logfile)) + (when val + (native-pathname (cast val c-string))))) + (declaim (inline dynamic-space-size)) + (defun dynamic-space-size () + (sb!alien:extern-alien "dynamic_space_size" sb!alien:unsigned-long))) ;;;; SUB-GC -;;; Used to carefully invoke hooks. -(eval-when (:compile-toplevel :execute) - (sb!xc:defmacro carefully-funcall (function &rest args) - `(handler-case (funcall ,function ,@args) - (error (cond) - (warn "(FUNCALL ~S~{ ~S~}) lost:~%~A" ',function ',args cond) - nil)))) - -;;; SUB-GC decides when and if to do a garbage collection. The FORCE-P -;;; flags controls whether a GC should occur even if the dynamic usage -;;; is not greater than *GC-TRIGGER*. +;;; SUB-GC does a garbage collection. This is called from three places: +;;; (1) The C runtime will call here when it detects that we've consed +;;; enough to exceed the gc trigger threshold. This is done in +;;; alloc() for gencgc or interrupt_maybe_gc() for cheneygc +;;; (2) The user may request a collection using GC, below +;;; (3) At the end of a WITHOUT-GCING section, we are called if +;;; *NEED-TO-COLLECT-GARBAGE* is true ;;; +;;; This is different from the behaviour in 0.7 and earlier: it no +;;; longer decides whether to GC based on thresholds. If you call +;;; SUB-GC you will definitely get a GC either now or when the +;;; WITHOUT-GCING is over + ;;; For GENCGC all generations < GEN will be GC'ed. -(defun sub-gc (&key force-p (gen 0)) - (/show0 "entering SUB-GC") - (unless *already-maybe-gcing* - (let* ((*already-maybe-gcing* t) - (start-time (get-internal-run-time)) - (pre-gc-dynamic-usage (dynamic-usage)) - ;; Currently we only check *SOFT-HEAP-LIMIT* at GC time, - ;; not for every allocation. That makes it cheap to do, - ;; even if it is a little ugly. - (soft-heap-limit-exceeded? (and *soft-heap-limit* - (> pre-gc-dynamic-usage - *soft-heap-limit*))) - (*soft-heap-limit* (if soft-heap-limit-exceeded? - (+ pre-gc-dynamic-usage - *bytes-consed-between-gcs*) - *soft-heap-limit*))) - (when soft-heap-limit-exceeded? - (cerror "Continue with GC." - "soft heap limit exceeded (temporary new limit=~D)" - *soft-heap-limit*)) - (when (and *gc-trigger* (> pre-gc-dynamic-usage *gc-trigger*)) - (setf *need-to-collect-garbage* t)) - (when (or force-p - (and *need-to-collect-garbage* (not *gc-inhibit*))) - ;; KLUDGE: Wow, we really mask interrupts all the time we're - ;; collecting garbage? That seems like a long time.. -- WHN 19991129 - (without-interrupts - ;; FIXME: We probably shouldn't do this evil thing to - ;; *STANDARD-OUTPUT* in a binding which is wrapped around - ;; calls to user-settable GC hook functions. - (let ((*standard-output* *terminal-io*)) - (when *gc-notify-stream* - (if (streamp *gc-notify-stream*) - (carefully-funcall *gc-notify-before* - *gc-notify-stream* - pre-gc-dynamic-usage) - (warn - "*GC-NOTIFY-STREAM* is set, but not a STREAM -- ignored."))) - (dolist (hook *before-gc-hooks*) - (carefully-funcall hook)) - (when *gc-trigger* - (clear-auto-gc-trigger)) - (let* (;; We do DYNAMIC-USAGE once more here in order to - ;; get a more accurate measurement of the space - ;; actually freed, since the messing around, e.g. - ;; GC-notify stuff, since the DYNAMIC-USAGE which - ;; triggered GC could've done a fair amount of - ;; consing.) - (pre-internal-gc-dynamic-usage (dynamic-usage)) - (ignore-me - #!-gencgc (funcall *internal-gc*) - ;; FIXME: This EQ test is pretty gross. Among its other - ;; nastinesses, it looks as though it could break if we - ;; recompile COLLECT-GARBAGE. We should probably just - ;; straighten out the interface so that all *INTERNAL-GC* - ;; functions accept a GEN argument (and then the - ;; non-generational ones just ignore it). - #!+gencgc (if (eq *internal-gc* #'collect-garbage) - (funcall *internal-gc* gen) - (funcall *internal-gc*))) - (post-gc-dynamic-usage (dynamic-usage)) - (n-bytes-freed (- pre-internal-gc-dynamic-usage - post-gc-dynamic-usage)) - ;; In sbcl-0.6.12.39, the raw N-BYTES-FREED from - ;; GENCGC could sometimes be substantially negative - ;; (e.g. -5872). I haven't looked into what causes - ;; that, but I suspect it has to do with - ;; fluctuating inefficiency in the way that the - ;; GENCGC packs things into page boundaries. - ;; Bumping the raw result up to 0 is a little ugly, - ;; but shouldn't be a problem, and it's even - ;; possible to sort of justify it: the packing - ;; inefficiency which has caused (DYNAMIC-USAGE) to - ;; grow is effectively consing, or at least - ;; overhead of consing, so it's sort of correct to - ;; add it to the running total of consing. ("Man - ;; isn't a rational animal, he's a rationalizing - ;; animal.":-) -- WHN 2001-06-23 - (eff-n-bytes-freed (max 0 n-bytes-freed))) - (declare (ignore ignore-me)) - (/show0 "got (DYNAMIC-USAGE) and EFF-N-BYTES-FREED") - (incf *n-bytes-freed-or-purified* - eff-n-bytes-freed) - (/show0 "clearing *NEED-TO-COLLECT-GARBAGE*") - (setf *need-to-collect-garbage* nil) - (/show0 "calculating NEW-GC-TRIGGER") - (let ((new-gc-trigger (+ post-gc-dynamic-usage - *bytes-consed-between-gcs*))) - (/show0 "setting *GC-TRIGGER*") - (setf *gc-trigger* new-gc-trigger)) - (/show0 "calling SET-AUTO-GC-TRIGGER") - (set-auto-gc-trigger *gc-trigger*) - (dolist (hook *after-gc-hooks*) - (/show0 "doing a hook from *AFTER-GC--HOOKS*") - ;; FIXME: This hook should be called with the same - ;; kind of information as *GC-NOTIFY-AFTER*. In - ;; particular, it would be nice for the hook function - ;; to be able to adjust *GC-TRIGGER* intelligently to - ;; e.g. 108% of total memory usage. - (carefully-funcall hook)) - (when *gc-notify-stream* - (if (streamp *gc-notify-stream*) - (carefully-funcall *gc-notify-after* - *gc-notify-stream* - post-gc-dynamic-usage - eff-n-bytes-freed - *gc-trigger*) - (warn - "*GC-NOTIFY-STREAM* is set, but not a stream -- ignored."))))) - (scrub-control-stack))) ;XXX again? we did this from C ... - (incf *gc-run-time* (- (get-internal-run-time) - start-time)))) - ;; FIXME: should probably return (VALUES), here and in RETURN-FROM - nil) - -;;; This routine is called by the allocation miscops to decide whether -;;; a GC should occur. The argument, OBJECT, is the newly allocated -;;; object which must be returned to the caller. -(defun maybe-gc (&optional object) - (sub-gc) - object) + +(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 + (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. + (sb!thread::without-thread-waiting-for (:already-without-interrupts t) + (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 gen) + (gc-stop-the-world) + (let ((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)))) + (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* + (sb!thread::without-thread-waiting-for () + (with-interrupts + (run-pending-finalizers) + (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) @@ -396,53 +310,156 @@ has finished GC'ing.") #!+(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 :force-p t :gen (if full 6 gen))) - + (when (sub-gc :gen (if full 6 gen)) + (post-gc))) + +(define-alien-routine scrub-control-stack sb!alien:void) + +(defun unsafe-clear-roots (gen) + #!-gencgc (declare (ignore gen)) + ;; 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) + ;; Clear caches depending on the generation being collected. + #!+gencgc + (cond ((eql 0 gen)) + ((eql 1 gen) + (ctype-of-cache-clear)) + (t + (drop-all-hash-caches))) + #!-gencgc + (drop-all-hash-caches)) ;;;; auxiliary functions (defun bytes-consed-between-gcs () #!+sb-doc - "Return the amount of memory that will be allocated before the next garbage - collection is initiated. This can be set with SETF." - *bytes-consed-between-gcs*) + "The amount of memory that will be allocated before the next garbage +collection is initiated. This can be set with SETF." + (sb!alien:extern-alien "bytes_consed_between_gcs" + (sb!alien:unsigned 32))) + (defun (setf bytes-consed-between-gcs) (val) - ;; FIXME: Shouldn't this (and the DECLAIM for the underlying variable) - ;; be for a strictly positive number type, e.g. - ;; (AND (INTEGER 1) FIXNUM)? (declare (type index val)) - (let ((old *bytes-consed-between-gcs*)) - (setf *bytes-consed-between-gcs* val) - (when *gc-trigger* - (setf *gc-trigger* (+ *gc-trigger* (- val old))) - (cond ((<= (dynamic-usage) *gc-trigger*) - (clear-auto-gc-trigger) - (set-auto-gc-trigger *gc-trigger*)) - (t - ;; FIXME: If SCRUB-CONTROL-STACK is required here, why - ;; isn't it built into SUB-GC? And *is* it required here? - (sb!sys:scrub-control-stack) - (sub-gc))))) - val) - -(defun gc-on () - #!+sb-doc - "Enable the garbage collector." - (setq *gc-inhibit* nil) - (when *need-to-collect-garbage* - (sub-gc)) - nil) - -(defun gc-off () - #!+sb-doc - "Disable the garbage collector." - (setq *gc-inhibit* t) - nil) - -;;;; initialization stuff - -(defun gc-reinit () - (when *gc-trigger* - (if (< *gc-trigger* (dynamic-usage)) - (sub-gc) - (set-auto-gc-trigger *gc-trigger*)))) + (setf (sb!alien:extern-alien "bytes_consed_between_gcs" + (sb!alien:unsigned 32)) + val)) + +(declaim (inline maybe-handle-pending-gc)) +(defun maybe-handle-pending-gc () + (when (and (not *gc-inhibit*) + (or #!+sb-thread *stop-for-gc-pending* + *gc-pending*)) + (sb!unix::receive-pending-interrupt))) + +;;;; GENCGC specifics +;;;; +;;;; For documentation convenience, these have stubs on non-GENCGC platforms +;;;; as well. +#!+gencgc +(deftype generation-index () + '(integer 0 #.sb!vm:+pseudo-static-generation+)) + +;;; FIXME: GENERATION (and PAGE, as seen in room.lisp) should probably be +;;; defined in Lisp, and written to header files by genesis, instead of this +;;; OAOOMiness -- this duplicates the struct definition in gencgc.c. +#!+gencgc +(define-alien-type generation + (struct generation + (alloc-start-page page-index-t) + (alloc-unboxed-start-page page-index-t) + (alloc-large-start-page page-index-t) + (alloc-large-unboxed-start-page page-index-t) + (bytes-allocated unsigned-long) + (gc-trigger unsigned-long) + (bytes-consed-between-gcs unsigned-long) + (number-of-gcs int) + (number-of-gcs-before-promotion int) + (cum-sum-bytes-allocated unsigned-long) + (minimum-age-before-gc double) + ;; `struct lutex *' or `void *', depending. + (lutexes (* char)))) + +#!+gencgc +(define-alien-variable generations + (array generation #.(1+ sb!vm:+pseudo-static-generation+))) + +(macrolet ((def (slot doc &optional setfp) + (declare (ignorable doc)) + `(progn + (defun ,(symbolicate "GENERATION-" slot) (generation) + #!+sb-doc + ,doc + #!+gencgc + (declare (generation-index generation)) + #!-gencgc + (declare (ignore generation)) + #!-gencgc + (error "~S is a GENCGC only function and unavailable in this build" + ',slot) + #!+gencgc + (slot (deref generations generation) ',slot)) + ,@(when setfp + `((defun (setf ,(symbolicate "GENERATION-" slot)) (value generation) + #!+gencgc + (declare (generation-index generation)) + #!-gencgc + (declare (ignore value generation)) + #!-gencgc + (error "(SETF ~S) is a GENCGC only function and unavailable in this build" + ',slot) + #!+gencgc + (setf (slot (deref generations generation) ',slot) value))))))) + (def bytes-consed-between-gcs + "Number of bytes that can be allocated to GENERATION before that +generation is considered for garbage collection. This value is meaningless for +generation 0 (the nursery): see BYTES-CONSED-BETWEEN-GCS instead. Default is +20Mb. Can be assigned to using SETF. Available on GENCGC platforms only. + +Experimental: interface subject to change." + t) + (def minimum-age-before-gc + "Minimum average age of objects allocated to GENERATION before that +generation is may be garbage collected. Default is 0.75. See also +GENERATION-AVERAGE-AGE. Can be assigned to using SETF. Available on GENCGC +platforms only. + +Experimental: interface subject to change." + t) + (def number-of-gcs-before-promotion + "Number of times garbage collection is done on GENERATION before +automatic promotion to the next generation is triggered. Can be assigned to +using SETF. Available on GENCGC platforms only. + +Experimental: interface subject to change." + t) + (def bytes-allocated + "Number of bytes allocated to GENERATION currently. Available on GENCGC +platforms only. + +Experimental: interface subject to change.") + (def number-of-gcs + "Number of times garbage collection has been done on GENERATION without +promotion. Available on GENCGC platforms only. + +Experimental: interface subject to change.")) + (defun generation-average-age (generation) + "Average age of memory allocated to GENERATION: average number of times +objects allocated to the generation have seen younger objects promoted to it. +Available on GENCGC platforms only. + +Experimental: interface subject to change." + #!+gencgc + (declare (generation-index generation)) + #!-gencgc (declare (ignore generation)) + #!-gencgc + (error "~S is a GENCGC only function and unavailable in this build." + 'generation-average-age) + #!+gencgc + (alien-funcall (extern-alien "generation_average_age" + (function double generation-index-t)) + generation))