antony at apocalypse.org
Tue Oct 22 15:27:14 EDT 2002
Thanks for the concise summary of the problem raised by shared thunks,
and for the "keepAlive" proposal.
> [btw will you be in New Haven around 16-19 Nov? I'm going to swing
> through there on my next trip over and it'd be good to see you and
> maybe ever humiliate myself again in the Gunks with you.]
[yes, I'll be around then, and look forward to much mutual humiliation
during your visit! :)]
> I think it was agreed that we need to replace touchForeignPtr with
> something else to let us express liveness dependencies. I hope you'll
> take part in that discussion since you and John Meacham are the only
> ones who seem to have used it so far.
You indicated that you were somewhat unclear why we need liveness
dependencies. I'll attempt to clarify by sketching some of the details
of the particular C library for which I am writing FFI wrappers.
I have a C library for 2D vector graphics. Two of the abstract types
provided by this C library are:
Pixmap -- A handle to an actual buffer of raster data
RenderContext -- A handle that encapsulates all state associated
with rendering, such as the current color, current font, target pixmap, etc.
Note that it is possible to create many RenderingContext's that all
render on to the same underlying Pixmap.
To see why we need liveness dependencies, consider the following typical
usage scenario in Haskell:
do pm <- createPixmap -- 1
rc <- createRenderContext pm -- 2
drawBox rc -- 3
Note that, in the above, it's possible that the call to
createRenderContext in line 2 could be the last Haskell reference to pm,
making it a candidate for collection. But we don't actually want the
Pixmap to be collected (and its finalizer invoked) until both the Pixmap
*and* all associated rendering contexts which refer to the Pixmap
The reason we need liveness dependencies is because, internally, the
RenderContext maintains a pointer to the target Pixmap. But because
this pointer exists only in the C heap, we need some way to inform
Haskell's garbage collector that whenever a particular RenderContext is
reachable, then its target pixmap is also reachable.
> As a strawman to get discussion rolling, would something like the
> following do the job?
Yes, almost. See below for details.
> -- |
> -- keepAlive x y ensures that the finalizer for y is not run
> -- until after the finalizer for x has run to completion.
> -- Of course, it might not even run then if y is still live
> -- at that point.
> keepAlive :: ForeignPtr a -> ForeignPtr b -> IO ()
> Off the top of my head, I'd say the name sucks, the argument order is
> open to change and the semantics will cause headaches for GHC. Any
> other objections? :-)
Henrik Nilsson and I had a good discussion about this over lunch,
particularly the subsequent issue about whether or not:
keepAlive p1 p2 >> keepAlive p2 p1
should create un-collectible garbage.
Our conclusion was that "finalization order" is a red herring, and that
this primitive should not try to make any kind of guarantees about
finalization order. All we need is some way to convey to Haskell's
collector that liveness of one particular ForeignPtr implies liveness of
another. I like your suggested "keepAlive" name, so I'd propose the
-- keepAlive x y informs Haskell's garbage collector that if x
-- is live, then y is also live.
keepAlive :: ForeignPtr a -> ForeignPtr b -> IO ()
Note that I have deliberately omitted any mention of the order in which
the finalizers are invoked.
Note, too, that in this version of keepAlive, it is perfectly reasonable
to do something like:
keepAlive p1 p2 >> keepAlive p2 p1
This will result in a cyclic structure that will be collected just like
any other cyclic structure, and p1 and p2's finalizers will both be
invoked, in some indeterminate order.
I can't speak for other users who might need some guarantees about
finalization order; I simply claim that the above definition is:
(a) adequate for capturing foreign liveness dependencies,
(b) allows for ForeignPtr's to objects that may have cyclic references in
their foreign representation.
I think that (b) is more important than guaranteeing any particular
finalization order, and that committing to a particular finalization
order is both difficult to implement and not very robust.
>>I would really like to have just a little bit more in-depth
>>understanding of why Haskell finalizers are an impossibility. In
>>particular, the current finalizers.txt document on cvs.haskell.org
Thank you very much for the clear, articulate summary. I think I
understand the issue(s) a bit better now.
Grad. Student, Dept. of Computer Science, Yale University
antony at apocalypse.org http://www.apocalypse.org/pub/u/antony
More information about the FFI