Re: Proposed change to ForeignPtr
Manuel wrote (snipped)
I have changed this in the spec now. I attach the wording used in the spec.
\item[newForeignPtr ::\ Ptr a -> FunPtr (Ptr a -> IO ()) -> IO (ForeignPtr a)] Turn a plain memory reference into a foreign object by associating a finalizer with the reference. The finalizer is represented by a pointer to an external function, which will be executed after the last reference to the foreign object is dropped. On invocation, the finalizer receives a pointer to the associated foreign object as an argument. Note that there is no guarantee on how soon the finalizer is executed after the last reference was dropped; this depends on the details of the Haskell storage manager. The only guarantee is that the finalizer runs before the program terminates.
Whether a finaliser may call back into the Haskell system is system dependent. Portable code may not rely on such call backs.
\item[addForeignPtrFinalizer ::\ ForeignPtr a -> FunPtr (Ptr a -> IO ()) -> IO ()] Add another finalizer to the given foreign object. No guarantees are made on the order in which multiple finalizers for a single object are run.
I think this is all a rather murky area. Consider two systems, let's call them Haskell and Foogle, which both operate heaps and do their own storage allocation, but also communicate over similar FFIs. We might very reasonably have situations where fairly complex inter-language pointers exist, so for example Haskell holds a ForeignPtr to something in the Foogle heap; the pointed-to Foogle object in turn references a Haskell object (presumably provided via StablePtr). Now suppose Haskell wants to drop the ForeignPtr. Then the logical thing for the finalizer to do is to tell Foogle that Haskell is no longer interested in the Foogle object. This then gives Foogle the chance on its own garbage collection to in turn drop the Haskell StablePtr. In turn this means somehow running StablePtr.freeStablePtr. However this scheme I don't know if that's legal, because the Haskell finalizer you need to run "freeStablePtr" is indirectly provoked by the initial Haskell finalizer. This is a pity, because this might actually be a fairly good way of managing garbage collection between Foogle and Haskell. Of course you would need at least reference counters (if you can guarantee there are no cycles containing both languages) or something more powerful otherwise, but reference counters at least can be provided. Furthermore I do actually have a real case in mind where I might use something like this, though I'd rather not go into details at this time. I'm afraid I haven't been following this thread lately, so I don't know what the big problem is about calling Haskell from a finalizer; I suppose it's something to do with us being in the middle of garbage collection. However wouldn't it be better to allow finalizers to somehow provide an action which may call Haskell, but (a) may be performed at some later date (such as when the GC is over); (b) consequently, may not assume that the pointer finalized still points to anything?
George Russell <ger@tzi.de> writes:
We might very reasonably have situations where fairly complex inter-language pointers exist, so for example Haskell holds a ForeignPtr to something in the Foogle heap; the pointed-to Foogle object in turn references a Haskell object (presumably provided via StablePtr). Now suppose Haskell wants to drop the ForeignPtr. Then the logical thing for the finalizer to do is to tell Foogle that Haskell is no longer interested in the Foogle object. This then gives Foogle the chance on its own garbage collection to in turn drop the Haskell StablePtr. In turn this means somehow running StablePtr.freeStablePtr. However this scheme I don't know if that's legal, because the Haskell finalizer you need to run "freeStablePtr" is indirectly provoked by the initial Haskell finalizer.
I don't see the problem. The Foogle garbage collector runs separately and asynchronously to the Haskell GC. A Foogle object is released by the Haskell collector, then at a later moment in time, a Haskell object is released by the Foogle collector. Where is the conflict?
Of course you would need at least reference counters (if you can guarantee there are no cycles containing both languages) or something more powerful otherwise, but reference counters at least can be provided.
Why do you suggest a need for reference counts? In the absence of cycles, surely the existing two garbage collectors (howsoever implemented) are sufficient.
I'm afraid I haven't been following this thread lately, so I don't know what the big problem is about calling Haskell from a finalizer; I suppose it's something to do with us being in the middle of garbage collection.
There is only a problem if Haskell is called directly by a finaliser since, as you noted, there is no heap available to run the finaliser. An indirect Haskell call triggered at some indeterminate point in the future, but in any case after the Haskell GC has completed, is no problem at all. Regards, Malcolm
I think this is all a rather murky area. Consider two systems, let's call them Haskell and Foogle, which both operate heaps and do their own storage allocation, but also communicate over similar FFIs.
This is indeed a murky area. Especially tricky when you have two GC's (even if one is nothing more than reference counting) is the issue of cycles that span the two systems: neither side is willing to release its pointer into the other world until the other side releases its pointer. (See section 5.2 of http://www.reid-consulting-uk.ltd.uk/alastair/publications/FP94.ps.gz for more on the problem and a sketch of a fix.)
We might very reasonably have situations where fairly complex inter-language pointers exist, so for example Haskell holds a ForeignPtr to something in the Foogle heap; the pointed-to Foogle object in turn references a Haskell object (presumably provided via StablePtr). Now suppose Haskell wants to drop the ForeignPtr. Then the logical thing for the finalizer to do is to tell Foogle that Haskell is no longer interested in the Foogle object. This then gives Foogle the chance on its own garbage collection to in turn drop the Haskell StablePtr. In turn this means somehow running StablePtr.freeStablePtr. However this scheme I don't know if that's legal, because the Haskell finalizer you need to run "freeStablePtr" is indirectly provoked by the initial Haskell finalizer.
We should provide a C function hs_freeStablePtr and explicitly say that it is safe to call this from inside a finalizer.
Of course you would need at least reference counters (if you can guarantee there are no cycles containing both languages) or something more powerful otherwise, but reference counters at least can be provided.
At least half the times I've used ForeignPtrs (aka ForeignObjs aka MallocPtrs), I've had to implement my own reference counting so, yes, it does seem like it'd be good to include reference counting in the implementation somehow. I don't remember ever finding a good way to do it though. The design seems obvious enough but it seems there's always some odd little wrinkle like the object already having its own reference counting implementation or the other world wanting to talk about foo_t* (i.e., the type the external library provides) instead of hs_stablePtr_t. In particular, if I put a wrapper around an object so that I can attach a reference counter, you somehow always find yourself faced with the problem that a C library hands you a pointer to an unwrapped object and you have to try to track down the reference counter for it. Any ideas welcome. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
George Russell <ger@tzi.de> wrote,
Manuel wrote (snipped)
I have changed this in the spec now. I attach the wording used in the spec.
\item[newForeignPtr ::\ Ptr a -> FunPtr (Ptr a -> IO ()) -> IO (ForeignPtr a)] Turn a plain memory reference into a foreign object by associating a finalizer with the reference. The finalizer is represented by a pointer to an external function, which will be executed after the last reference to the foreign object is dropped. On invocation, the finalizer receives a pointer to the associated foreign object as an argument. Note that there is no guarantee on how soon the finalizer is executed after the last reference was dropped; this depends on the details of the Haskell storage manager. The only guarantee is that the finalizer runs before the program terminates.
Whether a finaliser may call back into the Haskell system is system dependent. Portable code may not rely on such call backs.
\item[addForeignPtrFinalizer ::\ ForeignPtr a -> FunPtr (Ptr a -> IO ()) -> IO ()] Add another finalizer to the given foreign object. No guarantees are made on the order in which multiple finalizers for a single object are run.
I think this is all a rather murky area. Consider two systems, let's call them Haskell and Foogle, which both operate heaps and do their own storage allocation, but also communicate over similar FFIs. We might very reasonably have situations where fairly complex inter-language pointers exist, so for example Haskell holds a ForeignPtr to something in the Foogle heap; the pointed-to Foogle object in turn references a Haskell object (presumably provided via StablePtr). Now suppose Haskell wants to drop the ForeignPtr. Then the logical thing for the finalizer to do is to tell Foogle that Haskell is no longer interested in the Foogle object. This then gives Foogle the chance on its own garbage collection to in turn drop the Haskell StablePtr. In turn this means somehow running StablePtr.freeStablePtr. However this scheme I don't know if that's legal, because the Haskell finalizer you need to run "freeStablePtr" is indirectly provoked by the initial Haskell finalizer.
This is a pity, because this might actually be a fairly good way of managing garbage collection between Foogle and Haskell. Of course you would need at least reference counters (if you can guarantee there are no cycles containing both languages) or something more powerful otherwise, but reference counters at least can be provided. Furthermore I do actually have a real case in mind where I might use something like this, though I'd rather not go into details at this time.
I'm afraid I haven't been following this thread lately, so I don't know what the big problem is about calling Haskell from a finalizer; I suppose it's something to do with us being in the middle of garbage collection. However wouldn't it be better to allow finalizers to somehow provide an action which may call Haskell, but (a) may be performed at some later date (such as when the GC is over); (b) consequently, may not assume that the pointer finalized still points to anything?
There are good reasons why the 1.0 version of the FFI Addendum restricts itself to Haskell <-> C interaction and not more. The scenario that you sketch is one such reason. In other words, the kind of functionality that you want is currently not being supported by the standard FFI. (Nevertheless, system-specific extensions to the standard in GHC allow you to implement what you want). BTW, having two languages with separated heaps interact is a big mess as soon as you can have cycles, which you usually cannot exclude. Alastair already pointed that out and Martin Odersky also has nice stories to tell about this. Cheers, Manuel
Manuel M T Chakravarty wrote: [snip]
BTW, having two languages with separated heaps interact is a big mess as soon as you can have cycles, which you usually cannot exclude. Alastair already pointed that out and Martin Odersky also has nice stories to tell about this. [snip] Yeah yeah I know, indeed I think I pointed it out in the message which started this whole discussion. In the example application I am thinking of, I think cycles can be excluded in the first instance. In general I think there are various rather complicated things one might do in the finalizers to deal with cycles, and frankly I'd rather be able to write the code for this in Haskell rather than in C.
BTW, having two languages with separated heaps interact is a big mess as soon as you can have cycles, which you usually cannot exclude. Alastair already pointed that out and Martin Odersky also has nice stories to tell about this.
Hmmm, way back in '94, my thought was that the only thing to do in the presence of these cycles was to run the two GCs at once with a rather intimate communication between them where one says 'I can reach X' the other says 'ok, now I can reach Y', and eventually they both run out of objects to trace and they can discard unreached objects. The problem with this is that it runs into the same problems Malcolm and I are so keen to avoid: the other language has to be able to trigger GCs at more or less arbitrary times. Since then, I've read a lot about non-stop concurrent GC which doesn't need the two GCs to run simultaneously. Just thinking aloud, I wonder if it would be any easier to implement? The communication would be much the same ('I'm starting', 'I can reach X' and 'I'm done') but there'd be no need to synchronize the GCs. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
participants (4)
-
Alastair Reid -
George Russell -
Malcolm Wallace -
Manuel M T Chakravarty