On this page:
register_  finalizer
allocator
deallocator
0.45+9.2.0.3

3.1 Finalization🔗ℹ

 import: ffi/finalize package: rhombus-ffi-lib

function

fun finalize.register_finalizer(

  obj :: Any,

  finalizer :: Function.of_arity(1)

) :: Void

Registers a finalizer procedure finalizer for the given obj, which can be any Rhombus object. The finalizer procedure is called with obj as its argument sometime after the point where no safe Rhombus code can acess obj.

More precisely, the finalizer is registered with a “late” will executor that makes wills ready for a value only after all weak references (such as in a weak box) for the value have been cleared, which implies that the value is unreachable and no normal will executor has a will ready for the value. The finalizer is invoked when the will for obj becomes ready in the “late” will executor, which means that the value is unreachable (even from wills, and even from itself) by safe code.

The finalizer is invoked in a thread that is in charge of triggering will executors for finalize.register_finalizer. The given finalizer procedure should generally not rely on the environment of the triggering thread, and it must not use any parameters or call any parameter functions, except that relying on a default logger and/or calling Logger.current is allowed.

Finalizers are mostly intended to be used with pointer objects (for freeing unused memory that is not under GC control), but it can be used with any Rhombus object—even ones that have nothing to do with foreign code. Note, however, that the finalizer is registered for the Racket object that represents the pointer. If you intend to free a pointer object, then you must be careful to not register finalizers for two pointers that point to the same address. Also, be careful to not make the finalizer a closure that holds on to the object. Finally, beware that the finalizer is not guaranteed to be run when Rhombus exits.

function

fun finalize.allocator(

  dealloc :: maybe(Function.of_arity(1)),

  ~merely_uninterruptible: merely_uninterruptible :: Any.to_boolean = #false

) :: Function.assume_of((proc :: maybe(Function)) -> Any.like(proc))

Returns a function make_allocator. The generated make_allocator function, in turn, takes an argument f and returns an allocator procedure that behaves like f: it accepts the same arguments, and it produces the same result. Each result v from the allocator, if not #false, is registered with a finalizer that calls dealloc on vunless the call has been canceled by applying a deallocator (produced via finalize.deallocator) to v, in which case any existing dealloc registered for v is canceled. If and only if f is #false, foreign.allocator(dealloc)(alloc) produces #false.

The resulting allocator calls f in atomic mode, unless mereley_uninterruptible is true, in which case uninterruptible mode is used. The result from f is received and registered in atomic or uninterruptible mode, so that the result is reliably deallocated as long as no exception is raised.

The dealloc procedure will be called in atomic mode, and it must obey the same constraints as a finalizer procedure provided to finalize.register_finalizer. The dealloc procedure itself need not be a deallocator produced via finalize.deallocator. Along the same lines, if a deallocator is called explicitly on a v produced by allocator, it need not be the same as that allocator’s dealloc.

When a non-main place exits, after all custodian-shutdown actions, for every dealloc still registered via an allocator, the value to deallocate is treated as immediately unreachable. At that point, dealloc functions are called in reverse order of their registrations. Note that references in a dealloc function’s closure do not prevent running a dealloc function for any other value. If deallocation needs to proceed in an order different than reverse of allocation, use a retainer (via finalize.retainer) to insert a new deallocation action that will run earlier.

function

fun finalize.deallocator(

  get_arg :: Function.of_arity(1) = PairList.first,

  ~merely_uninterruptible: merely_uninterruptible :: Any.to_boolean = #false

) :: Function.assume_of((proc :: Function) -> Any.like(proc))

Returns a function make_deallocator. The generated make_deallocator function, in turn, takes an argument f and produces a deallocator procedure that behaves like f: it accepts the same arguments and returns the same result. The deallocator function calls dealloc in atomic mode or uninterruptible mode, and for one of its arguments, the it cancels the most recent remaining deallocator registered by an allocator or retainer.

The optional get_arg procedure determines which of f’s arguments correspond to the released object; get_arg receives a PairList of arguments passed to deallocator, so the default PairList.first selects the first one. Note that get_arg can only choose one of the by-position arguments to deallocator, although the deallocator function will require and accept the same keyword arguments as f, if any.