diff --git a/collects/ffi/unsafe.rkt b/collects/ffi/unsafe.rkt index 93bb391f85..b57927243a 100644 --- a/collects/ffi/unsafe.rkt +++ b/collects/ffi/unsafe.rkt @@ -1577,22 +1577,26 @@ regexp-replace regexp-replace*) (caar rs) str (cadar rs)) (cdr rs))))) -;; A facility for running finalizers using executors. The "stubborn" kind -;; of will executor is provided by '#%foreign, and it doesn't get GC'ed if -;; any finalizers are attached to it (while the normal kind can get GCed -;; even if a thread that is otherwise inaccessible is blocked on the executor). -;; Also it registers level-2 finalizers (which are run after non-late weak -;; boxes are cleared). -(define killer-executor (make-stubborn-will-executor)) (define killer-thread #f) -(define* (register-finalizer obj finalizer) - (unless killer-thread - (let ([priviledged-custodian ((get-ffi-obj 'scheme_make_custodian #f (_fun _pointer -> _scheme)) #f)]) - (set! killer-thread - (parameterize ([current-custodian priviledged-custodian] - ;; don't hold onto the namespace in the finalizer thread: - [current-namespace (make-base-empty-namespace)]) - (thread (lambda () - (let loop () (will-execute killer-executor) (loop)))))))) - (will-register killer-executor obj finalizer)) +(define* register-finalizer + ;; We bind `killer-executor' as a location variable, instead of a module + ;; variable, so that the loop for `killer-thread' doesn't have a namespace + ;; (via a prefix) in its continuation: + (let ([killer-executor (make-stubborn-will-executor)]) + ;; The "stubborn" kind of will executor (for `killer-executor') is + ;; provided by '#%foreign, and it doesn't get GC'ed if any + ;; finalizers are attached to it (while the normal kind can get + ;; GCed even if a thread that is otherwise inaccessible is blocked + ;; on the executor). Also it registers level-2 finalizers (which + ;; are run after non-late weak boxes are cleared). + (lambda (obj finalizer) + (unless killer-thread + (let ([priviledged-custodian ((get-ffi-obj 'scheme_make_custodian #f (_fun _pointer -> _scheme)) #f)]) + (set! killer-thread + (parameterize ([current-custodian priviledged-custodian] + ;; don't hold onto the namespace in the finalizer thread: + [current-namespace (make-base-empty-namespace)]) + (thread (lambda () + (let loop () (will-execute killer-executor) (loop)))))))) + (will-register killer-executor obj finalizer)))) diff --git a/collects/tests/gracket/draw-mem.rkt b/collects/tests/gracket/draw-mem.rkt new file mode 100644 index 0000000000..4b91409597 --- /dev/null +++ b/collects/tests/gracket/draw-mem.rkt @@ -0,0 +1,19 @@ +#lang racket + +;; Check for a leak via multiple `racket/draw' instantiations. + +(define-values (incs m) + (for/fold ([incs 0] [prev-mem 0]) ([i 10]) + (parameterize ([current-namespace (make-base-namespace)]) + (dynamic-require 'racket/draw #f)) + (collect-garbage) + (sync (system-idle-evt)) + (collect-garbage) + (let ([m (current-memory-use)]) + (if (m . > . (+ prev-mem (* 100 1024))) + (values (add1 incs) m) + (values incs m))))) + +(unless (incs . < . 5) + (error "multiple `racket/draw' instantiations seem to accumulate memory")) +