\f
;;;; 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-fun (lisp-fun c-var-name)
`(defun ,lisp-fun ()
(defun sub-gc (&key (gen 0))
(cond (*gc-inhibit*
- (setf *gc-pending* t))
+ (setf *gc-pending* t)
+ nil)
(t
(without-interrupts
(setf *gc-pending* :in-progress)
;; 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))
+ (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))
(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)))
+ (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
;; explicitly for a pending gc before interrupts are
;; enabled again.
(maybe-handle-pending-gc))
- ;; Outside the mutex, interrupts 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?
- ;;
- ;; 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*)
- (run-pending-finalizers)
- (call-hooks "after-GC" *after-gc-hooks* :on-error :warn)))))
+ 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)
+ (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)
#!+(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)))
+
+(define-alien-routine scrub-control-stack sb!alien:void)
(defun unsafe-clear-roots ()
;; KLUDGE: Do things in an attempt to get rid of extra roots. Unsafe
(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."
+ "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)))
(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))