On Thu, Jul 18, 2013 at 1:06 PM, Sandro Magi <[email protected]> wrote:
> Since we're establishing terminology, I suggest the following: > > 1. reclamation: return of resources to the pool, eg. memory, CPU schedule. > 2. finalization: explicit program code that runs before reclamation can > complete. > 3. disposal: cleanup of non-reclaimable resources, eg. file handles, > sockets, etc. > > My view of the above is as follows: > > +---------------------------+ > | Reclaimable | > | +-------------------+ | > | |Finalizable | | > | | +------------+ | | > | | | Disposable | | | > +---+---+------------+--+---+ > > That is, all disposable resources ought to have finalizers, and all > finalizable resources ought to have some reclaimable representation, > although not all finalizers manage disposable resources. > This terminology seems fine. I'm not sure, personally, whether "reclaimable" is a useful category, since *every* resource is reclaimable (we could quibble about CPU time, but never mind). My guess is that we'll eventually find it more useful to talk about specific *classes* of reclaimable objects (e.g. GC'd, RC'd, and so forth). But by all means let's see as we go. Task-local resources are guaranteed by the type system not to be shared > beyond the task's environment, so reference counting seems appropriate > for "prompt" resource reclamation, by which I mean specifically that the > task should not be marked "Done" until all resources it held are fully > disposed, finalized and reclaimed. I don't see how this is accounting for the distinction between voluntary and involuntary exit conditions. For involuntary exit (task is killed), the *only* actions that are guaranteed are the lower-layer dispose actions are taken. By definition, an involuntarily terminated task must not execute further instructions of any sort after the point of demise. For *voluntary* exit (including I request that your task exits and you comply), I like the concept you are proposing, but depending on the implementation of finalization there can be problems. As you know, there are many hopelessly broken implementations of finalization. In some (bad) approaches, it is possible for unreachable objects to become reachable again. In others, it is possible for a finalizer's execution to cause further objects to become free. This can lead to a requirement that the "GC then finalize" cycle may need to run multiple times before it converges. In the finalization design that is generally viewed as sane these days, finalization is performed by application code upon noticing that a weak reference in a table somewhere has reached its demise. Running that code from within the Exit() logic is problematic. *But*! This is voluntary exit. And that means we can rely an application to wrap itself in a catch block from which finalizers are run before the final Exit() call is made. In the end, what I'm really saying here is that the execution of finalization code during exit raises issues that aren't straightforward. I think we can achieve the objective you set, but I think we need to be a bit careful how we go about it. If an application really wants finalization to happen on exit, then I think that *one* of the following two statements must be true for every finalizer: 1. The finalization in question must be post-finalization, and the application must establish a catch point at which (1) no objects to be finalized are still live, and (2) the application explicitly invokes gc-and-finalize(), *or* 2. The finalizer body must be non-escaping and known to terminate. Such a finalizer can safely be run out-of-band on borrowed stack space. Note that such a finalizer can safely be run even on *involuntary* exit. Speaking generally, I think that the non-escaping rule is a good rule for both constructors and finalizers. For constructors, the requirement is that *this* not escape. For finalizers, it is necessary that no non-live object escape into the live set. In practice, this probably means that for finalizers no object references of any sort can escape. Reference counting achieves this with > no extra effort required, because full reclamation occurs upon the > task's finalizer completing. This should apply even in the event a task > is killed, though I'm not sure I've ever seen a satisfactory safe > semantics for killing tasks. > Reference counting for task resources isn't needed. There is single owning data structure - the task - and after task death no other reference can be outstanding for exclusively owned resources. Therefore the reference count must be one under your assumptions. Unfortunately your assumptions are problematic. As an example, consider a task that passes an opened file descriptor to another task. When that occurs, the underlying resource is now shared across multiple tasks and your assumption of exclusive ownership no longer holds. At that point, yes, reference counting becomes an appropriate solution. This is why POSIX uses a reference counting solution. Jonathan
_______________________________________________ bitc-dev mailing list [email protected] http://www.coyotos.org/mailman/listinfo/bitc-dev
