On Thu, Jul 18, 2013 at 8:09 PM, William ML Leslie <
[email protected]> wrote:

> However, if disposables cannot escape a task anyway, then disposables
> are task-local, so you already know (or, already can track) what needs
> to be disposed of at task exit.


Except, as I noted, that disposables *can* cross task boundaries and become
shared in real systems.


> In any case, for a large number of programs, there needs to be a way
> to specify finaliser order.


You do not *and cannot* get that guarantee out of any semantically sane
semantics of finalization. Finalizers, of necessity, execute in unspecified
order. If you need an order of disposition, you need to implement that in
the application. This is why the  .NET Dispose() pattern is so important.

This kind of problematic intuition is part of why finalizers are so
insidious. Necessary, but insidious. People assume certain things can or
should or will be done that aren't guaranteed in reality. Then they build
large, complex applications that rely on these presumed behaviors.
Behaviors that are poorly tested (and in some cases untestable) because
they occur only in conditions of extremis and are executed in unspecified
order.

As a last-gasp mechanism for what happens when an application fucks up
irrevocably, finalizers are better than leaking resources. But that's the
limit of what they are for.

Frankly, I'm tempted to introduce a requirement that objects which are
candidates for finalization must be reference counted, owned, or governed
by linear pointers. That way we know that the application defines a release
point for those objects. The problem with this is that it doesn't work.
Somebody will inevitably stick one of those pointers inside a leaked,
cyclic structure. The other option would be to declare that references to
finalized objects cannot live on the heap. That is: the cells containing
those references must live on the stack or in borrowed pointers at any
given time. That too is problematic; there are good reasons to want to
gather resources into collections, for example.

What needs to be achieved in the end is a set of object reference
propagation constraints that (transitively) guarantee release. The
specification of that condition needs to be recursive, in much the way that
confinement is recursive. If we were to specify this with reference
counting, the first part of the problem is to guarantee that every
reference-counted pointer is reachable by the stack, the second part is to
guarantee that they don't escape (so passing to borrowed pointers OK,
because those cannot be captured). The third part is the tricky one, which
is to guarantee that reference-counted pointers never form a cycle.  If we
could achieve those three conditions in the type system - and the first two
are straightforward - we're done.


 RAII is one way, weakref callbacks
> another,


RAII works fine. If you mean what I suspect by "weakref callbacks", that's *
not* fine - the result is a system that is not GC-safe.


Jonathan
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to