armload of DEFINE-HASH-CACHE changes
[sbcl.git] / src / code / gc.lisp
index f3b9615..fb4a0e1 100644 (file)
@@ -13,9 +13,6 @@
 \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 ()
@@ -159,6 +156,30 @@ run in any thread.")
   (defun gc-stop-the-world ())
   (defun gc-start-the-world ()))
 
+#!+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)))
 \f
 ;;;; SUB-GC
 
@@ -197,7 +218,8 @@ run in any thread.")
 
 (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)
@@ -214,18 +236,25 @@ run in any thread.")
            ;; 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))
                        (new-usage 0))
-                   (unsafe-clear-roots)
+                   (unsafe-clear-roots gen)
                    (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)))
+                     (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
@@ -247,21 +276,32 @@ run in any 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)
@@ -271,9 +311,12 @@ 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)))
+
+(define-alien-routine scrub-control-stack sb!alien:void)
 
-(defun unsafe-clear-roots ()
+(defun unsafe-clear-roots (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.
@@ -281,17 +324,22 @@ run in any thread.")
   ;; 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))
-
+  ;; 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))
 \f
 ;;;; 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."
+  "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)))
 
@@ -307,3 +355,111 @@ run in any thread.")
              (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))