AsyncFinalizers.jl

AsyncFinalizersModule

AsyncFinalizers

Dev CI

AsyncFinalizers.jl extends finalizer for

  • Allowing executing arbitrary code, including I/O, upon garbage collection of a given object.

  • Safe and unsafe APIs for avoiding escaping and thus "resurrecting" the object that would be collected otherwise.

For more information, see the documentation.

For how it works internally, see Implementation strategy.

API

  • AsyncFinalizers.onfinalize: like finalizer but allows I/O
  • AsyncFinalizers.unsafe_unwrap: unwrap the shim wrapper (see below)

Example

julia> using AsyncFinalizers

julia> mutable struct RefInt
           value::Int
       end

julia> object = RefInt(42);

julia> AsyncFinalizers.onfinalize(object) do shim
           # Unpack `shim` of the finalized `object`.  I/O is not allowed here.
           value = shim.value
           # Return a thunk:
           return function ()
               # Arbitrary I/O is possible here:
               println("RefInt(", value, ") is finalized")
           end
       end;

julia> object = nothing

julia> GC.gc(); sleep(0.1)
RefInt(42) is finalized

Note that the callback passed to AsyncFinalizers.onfinalize receives a shim wrapper and not the original object itself. To get the original object wrapped in shim, use AsyncFinalizers.unsafe_unwrap.

Implementation strategy

AsyncFinalizers.jl works internally by a background worker task that processes queued async finalizers (returned as thunks from the "on-finalize" callback registered using AsyncFinalizers.onfinalize) and a queue with lock-free put! called from the standard finalizer (the callback passed to Base.finalize). Since put! is lock-free in the "strict" sense (modulo GC), put! called in the standard finalizer can always eventually make forward progress independent of the state of the worker task at which it encounters the GC safepoint.

source
AsyncFinalizers.onfinalizeFunction
AsyncFinalizers.onfinalize(finalizer_factory, object)

Register asynchronous finalizer for an object.

The callback function finalizer_factory is a function with the following signature

finalizer_factory(shim) -> async_finalizer

i.e., finalizer_factory is a function that takes shim and returns async_finalizer where shim is an object that wraps the object and provides the same properties and async_finalizer is nothing or a callable that does not take any argument. The shim is valid only until finalizer_factory returns and not inside async_finalizer. As such, finalizer_factory must destruct shim and only capture the fields required for async_finalizer.

Use AsyncFinalizers.unsafe_unwrap to unwrap shim and obtain the original object. However, it is the user's responsibility to ensure that async_finalizer does not capture object.

The code executed in finalizer_factory should be as minimal as possible. In particular, no I/O is allowed inside of finalizer_factory.

source