Weak structure

The Weak structure contains functions for constructing weak references and arrays. A weak reference is a way of detecting when a resource is no longer in use and can be recovered. It is, in effect, a way of extending the concept of garbage-collection to user code.

signature WEAK =
    val weak: 'a ref option -> 'a ref option ref
    val weakArray: int * 'a ref option -> 'a ref option array
    val weakLock: Thread.Mutex.mutex
    and weakSignal: Thread.ConditionVar.conditionVar

structure Weak: WEAK;
val weak: 'a ref option -> 'a ref option ref

Constructs a weak reference.

val weakArray: int * 'a ref option -> 'a ref option array

Constructs an array containing weak references.

val weakLock: Thread.Mutex.mutex
val weakSignal: Thread.ConditionVar.conditionVar

A lock and a condition variable that is broadcast when the garbage collector has recovered a token.

The idea behind weak references is to allow user library code to recover resources when they are no longer in use. This is only relevant for resources, such as file descriptors, that exist outside the Poly/ML memory and need to be recovered.

The garbage-collector recovers space in the heap by identifying cells that are reachable from roots, generally the stacks of threads, and treating everything else as garbage. This can be extended to external resources by associating a token with the resource. While the token is reachable the resource is considered to be in use. Once the token ceases to be reachable the resource can be recovered.

A weak reference is used to detect when the token is no longer accessible. To make use of this the library code must allocate a normal reference value, the token, whenever it constructs or links to the external resource and include the token within the data it returns to the client code. The contents of the reference are not relevant; it can be a unit ref, what matters is the identity of the reference. When the library creates a token it makes an entry in its own data structure within a weak reference or array. That entry is set to SOME token. Note that the type of a weak reference is 'a ref option ref i.e. it can only contain an option type holding a reference value.

Provided the client code continues to use the resource and has a reachable pointer to the token there will be no change to the state. If, though, it discards the data associated with the resource and hence the pointer to the token the resource is considered to be released and the library may recover the resource. If the garbage collector detects that there are no other pointers to the token except the weak reference it will change the weak reference from SOME token to NONE, so there are no longer any pointers at all.

To actually release the external resource the library must check the weak references or arrays within its own data structures and look for entries that have been set to NONE. Depending how the library code works it may be appropriate to do this synchronously whenever a request is made to allocate a new resource. An alternative would be to create a new thread to manage the process asynchronously. To aid this the thread should lock the weakLock mutex and suspend itself by calling Thread.ConditionVar.wait or Thread.ConditionVar.waitUntil, passing weakLock and weakSignal as arguments. The weakSignal condition variable is broadcast after a garbage-collection if the garbage collector has modified a weak reference. Because there may be several libraries using weak references the receipt of the signal does not guarantee that a resource associated with any particular library has been released.

The garbage-collector is only run when necessary and detection of released resources may happen very infrequently, depending on factors such as the size of the heap. To force a collection the library can call PolyML.fullGC.