ffi/unsafe/custodian: add success callback to receive unregister function
Also, correct some information in the documentation of `register-custodian-shutdown`. Closes #3841
This commit is contained in:
parent
64f0c8a7ed
commit
5f63632f8a
|
@ -26,11 +26,15 @@ a pointer that can be supplied to
|
||||||
If @racket[at-exit?] is true, then @racket[callback] is applied when
|
If @racket[at-exit?] is true, then @racket[callback] is applied when
|
||||||
Racket exits, even if the custodian is not explicitly shut down.
|
Racket exits, even if the custodian is not explicitly shut down.
|
||||||
|
|
||||||
If @racket[weak?] is true, then @racket[callback] may not be called
|
If @racket[weak?] is true, then @racket[callback] may not be called if
|
||||||
if @racket[v] is determined to be unreachable during garbage
|
@racket[v] is determined to be unreachable during garbage collection.
|
||||||
collection. The value @racket[v] is always weakly held by the
|
The value @racket[v] is initially weakly held by the custodian, even
|
||||||
custodian, even if @racket[weak?] is @racket[#f]; see
|
if @racket[weak?] is @racket[#f]. A value associated with a custodian
|
||||||
@cpp{scheme_add_managed} for more information.
|
can therefore be finalized via will executors, at least through will
|
||||||
|
registrations and @racket[register-finalizer] uses @emph{after}
|
||||||
|
calling @racket[register-custodian-shutdown], but the value becomes
|
||||||
|
strongly held when no there are no other strong references and no
|
||||||
|
later-registered finalizers or wills apply.
|
||||||
|
|
||||||
If @racket[ordered?] is true when @racket[weak] is @racket[#f], then
|
If @racket[ordered?] is true when @racket[weak] is @racket[#f], then
|
||||||
@racket[v] is retained in a way that allows finalization of @racket[v]
|
@racket[v] is retained in a way that allows finalization of @racket[v]
|
||||||
|
@ -39,7 +43,7 @@ via @racket[register-finalizer] to proceed.
|
||||||
Normally, @racket[weak?] should be false. To trigger actions based on
|
Normally, @racket[weak?] should be false. To trigger actions based on
|
||||||
finalization or custodian shutdown---whichever happens first---leave
|
finalization or custodian shutdown---whichever happens first---leave
|
||||||
@racket[weak?] as @racket[#f] and have a finalizer run in atomic mode
|
@racket[weak?] as @racket[#f] and have a finalizer run in atomic mode
|
||||||
and cancel the shutdown action via
|
to check that the custodian shutdown has not happened and then cancel the shutdown action via
|
||||||
@racket[unregister-custodian-shutdown]. If @racket[weak?] is true or
|
@racket[unregister-custodian-shutdown]. If @racket[weak?] is true or
|
||||||
if the finalizer is not run in atomic mode, then there's no guarantee
|
if the finalizer is not run in atomic mode, then there's no guarantee
|
||||||
that either of the custodian or finalizer callbacks has completed by
|
that either of the custodian or finalizer callbacks has completed by
|
||||||
|
@ -48,7 +52,7 @@ be no longer registered to the custodian, while the finalizer for
|
||||||
@racket[v] might be still running or merely queued to run.
|
@racket[v] might be still running or merely queued to run.
|
||||||
Furthermore, if finalization is via @racket[register-finalizer] (as
|
Furthermore, if finalization is via @racket[register-finalizer] (as
|
||||||
opposed to a @tech[#:doc reference.scrbl]{will executor}), then supply
|
opposed to a @tech[#:doc reference.scrbl]{will executor}), then supply
|
||||||
@racket[ordered?] as true; @racket[ordered?] is false while
|
@racket[ordered?] as true; if @racket[ordered?] is false while
|
||||||
@racket[weak?] is false, then @racket[custodian] may retain @racket[v]
|
@racket[weak?] is false, then @racket[custodian] may retain @racket[v]
|
||||||
in a way that does not allow finalization to be triggered when
|
in a way that does not allow finalization to be triggered when
|
||||||
@racket[v] is otherwise inaccessible. See also
|
@racket[v] is otherwise inaccessible. See also
|
||||||
|
@ -71,7 +75,8 @@ is taken.}
|
||||||
[callback (any/c . -> . any)]
|
[callback (any/c . -> . any)]
|
||||||
[custodian custodian? (current-custodian)]
|
[custodian custodian? (current-custodian)]
|
||||||
[#:at-exit? at-exit? any/c #f]
|
[#:at-exit? at-exit? any/c #f]
|
||||||
[#:custodian-unavailable unavailable-callback ((-> void?) -> any) (lambda (reg-fnl) (reg-fnl))])
|
[#:custodian-available available-callback ((any/c -> void?) -> any) (lambda (_unreg) (void))]
|
||||||
|
[#:custodian-unavailable unavailable-callback ((-> void?) -> any) (lambda (_reg-fnl) (_reg-fnl))])
|
||||||
any]{
|
any]{
|
||||||
|
|
||||||
Registers @racket[callback] to be applied (in atomic mode) to
|
Registers @racket[callback] to be applied (in atomic mode) to
|
||||||
|
@ -82,14 +87,25 @@ object @racket[v] is subject to the the constraints of
|
||||||
@racket[register-finalizer]---particularly the constraint that
|
@racket[register-finalizer]---particularly the constraint that
|
||||||
@racket[v] must not be reachable from itself.
|
@racket[v] must not be reachable from itself.
|
||||||
|
|
||||||
|
When @racket[v] is successfully registered with @racket[custodian] and
|
||||||
|
a finalizer is registered, then @racket[available-callback] is called
|
||||||
|
with a function @racket[_unreg] that unregisters the @racket[v] and
|
||||||
|
disables the use of @racket[callback] through the custodian or a
|
||||||
|
finalizer. The value @racket[v] must be provided to @racket[_unreg]
|
||||||
|
(otherwise it would be in @racket[_unreg]'s closure, possibly
|
||||||
|
preventing the value from being finalized). The
|
||||||
|
@racket[available-callback] function is called in tail position, so
|
||||||
|
its result is the result of
|
||||||
|
@racket[register-finalizer-and-custodian-shutdown].
|
||||||
|
|
||||||
If @racket[custodian] is already shut down, then
|
If @racket[custodian] is already shut down, then
|
||||||
@racket[unavailable-callback] is applied in tail position to a
|
@racket[unavailable-callback] is applied in tail position to a
|
||||||
function that registers a finalizer. By default, a finalizer is
|
function @racket[reg-fnl] that registers a finalizer. By default, a
|
||||||
registered anyway, but usually a better choice is to report an error.
|
finalizer is registered anyway, but usually a better choice is to
|
||||||
If @racket[custodian] is not already shut down, then the result
|
report an error.
|
||||||
from @racket[register-finalizer-and-custodian-shutdown] is @|void-const|.
|
|
||||||
|
|
||||||
@history[#:added "6.1.1.6"]}
|
@history[#:added "6.1.1.6"
|
||||||
|
#:changed "8.1.0.6" @elem{Added the @racket[#:custodian-available] argument.}]}
|
||||||
|
|
||||||
|
|
||||||
@defproc[(make-custodian-at-root) custodian?]{
|
@defproc[(make-custodian-at-root) custodian?]{
|
||||||
|
|
|
@ -77,3 +77,39 @@
|
||||||
(error "custodian-shutdown callback wasn't called"))
|
(error "custodian-shutdown callback wasn't called"))
|
||||||
|
|
||||||
(unregister-custodian-shutdown 'anything #f)
|
(unregister-custodian-shutdown 'anything #f)
|
||||||
|
|
||||||
|
;; ----------------------------------------
|
||||||
|
;; Check unregistration callback after successful register
|
||||||
|
|
||||||
|
(let ([c2 (make-custodian)]
|
||||||
|
[val (gensym)]
|
||||||
|
[ran? #f])
|
||||||
|
(define unreg
|
||||||
|
(register-finalizer-and-custodian-shutdown
|
||||||
|
val (lambda (v) (set! ran? #t)) c2
|
||||||
|
#:custodian-available (lambda (unreg) unreg)))
|
||||||
|
(unless (and (procedure? unreg)
|
||||||
|
(procedure-arity-includes? unreg 1))
|
||||||
|
(error "custodian-shutdown unregister is not a suitable procedure"))
|
||||||
|
(unreg val)
|
||||||
|
(custodian-shutdown-all c2)
|
||||||
|
(when ran?
|
||||||
|
(error "custodian-shutdown unregister did not work")))
|
||||||
|
|
||||||
|
;; check that the unregister function doesn't retain the value:
|
||||||
|
(let ([c2 (make-custodian)]
|
||||||
|
[val (gensym)])
|
||||||
|
(define unreg
|
||||||
|
(register-finalizer-and-custodian-shutdown
|
||||||
|
val void c2
|
||||||
|
#:custodian-available (lambda (unreg) unreg)))
|
||||||
|
(unless (eq? 'cgc (system-type 'gc))
|
||||||
|
(let ([we (make-will-executor)]
|
||||||
|
[done? #f])
|
||||||
|
(will-register we val (lambda (val)
|
||||||
|
(unreg val)
|
||||||
|
(set! done? #t)))
|
||||||
|
(collect-garbage)
|
||||||
|
(unless (and (will-try-execute we)
|
||||||
|
done?)
|
||||||
|
(error "will wasn't ready")))))
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
(define (register-finalizer-and-custodian-shutdown value callback
|
(define (register-finalizer-and-custodian-shutdown value callback
|
||||||
[custodian (current-custodian)]
|
[custodian (current-custodian)]
|
||||||
#:at-exit? [at-exit? #f]
|
#:at-exit? [at-exit? #f]
|
||||||
#:custodian-unavailable [custodian-unavailable (lambda (r) (r))])
|
#:custodian-unavailable [custodian-unavailable (lambda (r) (r))]
|
||||||
|
#:custodian-available [success-k (lambda (unregister) (void))])
|
||||||
(define done? #f)
|
(define done? #f)
|
||||||
(define (do-callback obj) ; called in atomic mode
|
(define (do-callback obj) ; called in atomic mode
|
||||||
(unless done?
|
(unless done?
|
||||||
|
@ -43,6 +44,13 @@
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(unregister-custodian-shutdown obj registration)
|
(unregister-custodian-shutdown obj registration)
|
||||||
(do-callback obj))))))
|
(do-callback obj))))))
|
||||||
(if registration
|
(cond
|
||||||
(do-finalizer)
|
[registration
|
||||||
(custodian-unavailable do-finalizer)))
|
(do-finalizer)
|
||||||
|
(success-k (lambda (obj)
|
||||||
|
(call-as-atomic
|
||||||
|
(lambda ()
|
||||||
|
(set! done? #t)
|
||||||
|
(unregister-custodian-shutdown obj registration)))))]
|
||||||
|
[else
|
||||||
|
(custodian-unavailable do-finalizer)]))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user