<< Prev | - Up - | Next >> |
Finalize
The finalization facility gives the programmer the ability to process an Oz value when it is discovered (during garbage collection) that it has otherwise become unreachable. This is often used to release native resources held or encapsulated by said value.
Native functors and extension classes make it very easy to extend Oz with new interfaces to arbitrary resources. Typically, the access to a resource will be encapsulated into a data-structure that is an instance of an extension class and we would like the resource to be automatically released when the data-structure is no longer being referenced and can be garbage collected. For example:
we may encapsulate a handle to access a database: when the handle is garbage collected, we would like the connection to the database to be automatically closed.
we may encapsulate a pointer to malloc'ed memory, e.g. for a large bitmap to be manipulated by native DLLs: when this is no longer referenced, we would like the memory to be automatically freed.
This is the purpose of finalization: to execute a cleanup action when a computational object is no longer referenced. The Oz finalization facility was inspired by the article Guardians in a Generation-Based Garbage Collector (R. Kent Dybvig, Carl Bruggeman, David Eby, June 1993). It is built on top of weak dictionaries (Section 9.4 of ``The Oz Base Environment''): each weak dictionary is associated with a finalization stream on which pairs Key#Value
appear when Value
becomes unreachable except through one or more weak dictionaries.
Finalize.guardian
{Finalize.guardian
+Finalizer
?Register
}
This takes as input a 1-ary procedure Finalizer
and returns a 1-ary procedure Register
representing a new guardian. A value X
can be registered in the guardian using:
{Register X}
Thereafter, when X
becomes unreachable except through a weak dictionary (e. g. a guardian), at the next garbage collection X
is removed from the guardian and the following is eventually executed:
{Finalizer X}
We say eventually because the finalization thread is subject to the same fair scheduling as any other thread. Note that the value X
has still not been garbage collected; only at the next garbage collection after the call to Finalizer
has returned, and assuming Value
is no longer referenced at all, not even by the guardian, will it really be garbage collected.
Finalize.register
{Finalize.register
+Value
+Handler
}
This is a slightly different interface that allows to register simultaneously a Value
and a corresponding finalizer procedure Handler
. After Value
becomes otherwise unreachable, {
Handler
Value
}
is eventually executed.
Finalize.everyGC
{Finalize.everyGC
+P/0
}
This simply registers a 0-ary procedure to be invoked after each garbage collection. Note that you cannot rely on how soon after the garbage collection this procedure will really be invoked: it is in principle possible that the call may only be scheduled several garbage collections later if the system has an incredibly large number of live threads and generates tons of garbage. It is instructive to look at the definition of EveryGC
:
proc {EveryGC P}
proc {DO _} {P} {Finalize.register DO DO} end
in {Finalize.register DO DO} end
in other words, we create a procedure DO
and register it using itself as its own handler. When invoked, it calls P
and registers itself again.
<< Prev | - Up - | Next >> |