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
