A test no longer fails.
[sbcl.git] / tests / finalize.impure.lisp
blobd9cee0232e32e2e30e434b99fba2781877227c49
1 #+(or sb-safepoint interpreter) (invoke-restart 'run-tests::skip-file)
3 (defvar *tmp* 0.0) ; don't remove - used by the setq below
4 (defglobal *count* 0)
5 (declaim (fixnum *count*))
7 (defun foo (_)
8 (declare (ignore _))
9 nil)
11 (defglobal *maxdepth* 0)
12 ;; Be gentler on 32-bit platforms
13 (defglobal *n-finalized-things* (or #+(and 64-bit (not sb-safepoint)) 20000 10000))
14 (defglobal *weak-pointers* nil)
16 (defun makejunk (_)
17 (declare (ignore _))
18 (let ((x (gensym)))
19 (push (make-weak-pointer x) *weak-pointers*)
20 (finalize x (lambda ()
21 (setq *maxdepth*
22 (max sb-kernel:*free-interrupt-context-index*
23 *maxdepth*))
24 ;; cons 320K in the finalizer for #+64-bit,
25 ;; or 80K for #-64-bit
26 (setf *tmp* (make-list *n-finalized-things*))
27 (sb-ext:atomic-incf *count*)))
28 x))
29 (compile 'makejunk)
31 (defun scrubstack ()
32 (sb-int:dx-let ((b (make-array 20))) (eval b))
33 (sb-sys:scrub-control-stack))
35 (defun run-consy-thing ()
36 (dotimes (iter 10)
37 (let ((junk (mapcar #'makejunk
38 (make-list (/ *n-finalized-things* 10)))))
39 (setf junk (foo junk))
40 (foo junk))
41 (scrubstack)
42 (gc :full t)))
44 ; no threads - hope for the best with respect to conservative root retention
45 #-sb-thread (run-consy-thing)
47 #+sb-thread
48 (progn
49 (sb-thread:join-thread (sb-thread:make-thread #'run-consy-thing))
50 (gc :full t)) ; one more time to clean everything up
52 ;;; Verify that the thread was started.
53 #+sb-thread
54 (with-test (:name :finalizer-thread-started)
55 (assert (typep sb-impl::*finalizer-thread* 'sb-thread::thread))
56 ;; We're going to assert on an approximate count of finalizers that ran,
57 ;; which is a bit sketchy.
58 ;; So call RUN-PENDING-FINALIZERS which actually does the work of helping out
59 ;; the finalizer thread.
60 ;; This also shows that it works to run finalization actions in two threads -
61 ;; the finalizer thread and this. Hence the need for ATOMIC-INCF on *count*.
62 (sb-impl::run-pending-finalizers)
64 ;; Make sure the thread is done.
65 ;; This JOIN-THREADs it, so we know it's not executing.
66 (sb-impl::finalizer-thread-stop))
68 ;;; This was failing with something like:
69 ;;; The assertion (<= *MAXDEPTH* 1) failed with *MAXDEPTH* = 232.
70 ;;; The test parameters for 64-bit are quite severe, but should not exhaust the heap.
71 (with-test (:name :finalizers-dont-nest-garbage-collections)
72 (assert (<= *maxdepth* 1)))
74 (with-test (:name :finalizers-ran)
75 ;; Finalizers won't have run for keys needing to be rehashed.
76 (unless (null sb-impl::*finalizer-rehashlist*)
77 (format t "~&::: INFO: rehashing finalizer store~%")
78 (sb-impl::finalizers-rehash)
79 (gc)
80 (sb-impl::run-pending-finalizers))
81 ;; expect that 97% of the finalizers ran
82 (assert (>= *count* (* *n-finalized-things* 97/100)))
83 (unless (= *count* *n-finalized-things*)
84 ;; show how the junk was reachable
85 (search-roots *weak-pointers* :print :verbose)))
87 ;;; For finalizers that didn't run (if any), we had better find
88 ;;; that the object still exists in *weak-pointers*.
89 ;;; Conversely, for each intact weak pointer, there had better be
90 ;;; a finalizer for that object.
91 ;;; #+weak-vector-readbarrier could be made to pass, but scarcely seems
92 ;;; worth the effort since finalizers work if and only if weak-pointers do.
93 (with-test (:name :finalizer-state
94 :skipped-on :weak-vector-readbarrier)
95 (setq *weak-pointers*
96 (delete-if (lambda (x) (null (weak-pointer-value x)))
97 *weak-pointers*))
98 (let ((solist sb-impl::**finalizer-store**))
99 (sb-lockless:so-maplist
100 (lambda (node)
101 (let ((k (sb-sys:without-gcing (sb-impl::finalizer-object node))))
102 (when (and (symbolp k) (not (symbol-package k)))
103 (assert (find k *weak-pointers* :key #'weak-pointer-value)))))
104 solist)
105 (sb-sys:without-gcing
106 (dolist (wp *weak-pointers*)
107 (let ((addr (sb-kernel:%make-lisp-obj
108 (logandc2 (sb-kernel:get-lisp-obj-address
109 (weak-pointer-value wp))
110 sb-vm:lowtag-mask))))
111 (assert (sb-lockless:so-find solist addr)))))))
113 ;;; A super-smart GC and/or compiler might prove that the object passed to
114 ;;; FINALIZE is instantly garbage. Like maybe (FINALIZE (CONS 1 2) 'somefun)
115 ;;; with the object otherwise unreachable. And so the system might reasonably
116 ;;; call #'SOMEFUN right away. Hence it had better be a function so that the
117 ;;; error isn't just shoved over to the finalizer thread.
118 ;;; Also, as a special case caught by the general case, I'd rather not see NILs
119 ;;; in the finalizer table, because NIL could never be defined as a function.
120 (with-test (:name :finalizer-funarg)
121 ;; The :no-function-conversion option in fndb was removed, for two reasons:
122 ;; - unlike with SET-MACRO-CHARACTER there is no "inquiry" function that you
123 ;; could use to determine what symbol was given as the funarg.
124 ;; Whereas I prefer lazy resolution in readtables because GET-MACRO-CHARACTER
125 ;; should return 'FUN if that's what you had set, and not #'FUN.
126 ;; - I can't particularly see anyone making the claim that a weird use such as
127 ;; (FINALIZE (CONS 1 2) 'STRING=))) should not emit e a warning about #'STRING=
128 ;; being funcalled with zero args when it should receive two.
129 (assert (nth-value 1 (compile nil '(lambda () (finalize (cons 1 2) 'string=)))))
130 ;; I make this mistake sometimes in GC testing - forgetting to wrap
131 ;; a FORMAT in a LAMBDA. So this had better fail early and often.
132 (assert-error (finalize (cons 'a 'b) (format t "The object died~%")))
133 ;; This too, even if you think this is a little different, because
134 ;; you _could_ define this function. But it's not different.
135 ;; The attempted call to #'no-function-for-you may arbitrarily occur
136 ;; (in theory) as soon as the system is able to detect garbage.
137 (assert-error (finalize (cons 1 2) 'no-function-for-you)))