
Thanks for your reply. I'm afraid it's left me even more confused about which way to go with this :-(
If it's possible that future Haskell FFI's don't guarantee that all finalisers are run then this more or less rules out the use of the reference counting solution (which wasn't particularly attractive anyway because it needs to be done in C AFAICS :-). If users who want this behaviour are required to code it themselves, it seems to require that they maintain a global list of all allocated ForeignPtrs. But doing that naively will stop them being garbage collected at all, unless it's possible to do something clever using weak pointers. Perhaps it is possible (or maybe some tricks at the C level could be used) but I think it's a significant extra burden for FFI users.
Yes, it would have to be a global list of weak pointers to ForeignPtrs. This topic has come up before, though not on this list. See this message, and the rest of the thread: http://www.haskell.org/pipermail/cvs-ghc/2003-January/016651.html the thread also moved on to ffi@haskell.org: http://www.haskell.org/pipermail/ffi/2003-January/001041.html and be sure to check out the paper by Hans Boehm referenced in that message, it's a good summary of the issues involved.
Also, while we're talking about this, maybe the semantics of performGC should be clarified. Does it block until all GC (and finalisation of garbage ForeignPtrs) is complete? I would guess this was the original intention, but this doesn't seem to be consistent with non-stop Haskell. If it does block, are all Haskell threads blocked, or just the calling thread?
performGC doesn't do anything that you can rely on :-) In practice, it probably starts all the finalizers that are ready to run, but it certainly doesn't wait for their termination.
Also, I could you explain what you mean by a suitable exception handler? I don't really understand this at all. I'd expected I may well end up using bracket or similar, but I'm not sure how exception handling is relevant to this problem.
Start your program something like this: import Control.Exception (finally) main = my_main `finally` clean_up my_main = ... put your program here ... clean_up = ... all the cleanup code goes here ... You can additionally use finalizers to perform incremental cleanup during program execution, but the right way to clean up at the end is to use an exception handler as above. Cheers, Simon

On Mon, Dec 22, 2003 at 10:13:42AM -0000, Simon Marlow wrote:
performGC doesn't do anything that you can rely on :-) In practice, it probably starts all the finalizers that are ready to run, but it certainly doesn't wait for their termination.
Moreover, it is not guaranteed that performGC will trigger a full GC. Most often it will reclaim only the 0th generation. I think it would be useful to have something like performFullGC. Best regards, Tom -- .signature: Too many levels of symbolic links

On Mon, Dec 22, 2003 at 11:34:14AM +0100, Tomasz Zielonka wrote:
Moreover, it is not guaranteed that performGC will trigger a full GC. Most often it will reclaim only the 0th generation. I think it would be useful to have something like performFullGC.
Or rather performMajorGC :) It seems that in GHC it suffices to foreign import ccall performMajorGC :: IO () Best regards, Tom -- .signature: Too many levels of symbolic links

On Monday 22 Dec 2003 8:53 pm, Carl Witty wrote:
Thanks for your reply. I'm afraid it's left me even
more confused about which way to go with this :-(
Is your problem something you could handle with a C atexit() handler?
That's a good idea. With ghc I guess this will work, assuming.. 1- ghc rts runs all ForeignPtr finalisers before it shutsdown. 2- ghc rts is shutdown before atexit handlers are executed. I both think 1 & 2 are true with ghc at present, but Simon M. indicated that 1 might not be true in future for ghc (or other Haskell implementations). That said, the current FFI spec states at bottom of p.14.. "There is no guarantee on how soon the finalizer is executed after the last reference to the associated foreign pointer was dropped; this depends on the details of the Haskell storeage manager. The only guarantee is that the finalizer runs before the program terminates." So I'm still confused :-) Actually, though I think it would work for me, it's probably not as general as some folk might want (they might want to shutdown the library and free up whatever resources it claimed earlier in program execution, not just at exit). Regards -- Adrian Hey

On Monday 22 Dec 2003 10:13 am, Simon Marlow wrote:
Thanks for your reply. I'm afraid it's left me even more confused about which way to go with this :-(
If it's possible that future Haskell FFI's don't guarantee that all finalisers are run then this more or less rules out the use of the reference counting solution (which wasn't particularly attractive anyway because it needs to be done in C AFAICS :-). If users who want this behaviour are required to code it themselves, it seems to require that they maintain a global list of all allocated ForeignPtrs. But doing that naively will stop them being garbage collected at all, unless it's possible to do something clever using weak pointers. Perhaps it is possible (or maybe some tricks at the C level could be used) but I think it's a significant extra burden for FFI users.
Yes, it would have to be a global list of weak pointers to ForeignPtrs. This topic has come up before, though not on this list. See this message, and the rest of the thread:
http://www.haskell.org/pipermail/cvs-ghc/2003-January/016651.html
the thread also moved on to ffi@haskell.org:
http://www.haskell.org/pipermail/ffi/2003-January/001041.html
and be sure to check out the paper by Hans Boehm referenced in that message, it's a good summary of the issues involved.
Thanks, I'll take a look at the Boehm paper. I didn't keep up with this discussion at the time, but now I see the relevance. Assuming the weak pointers solution is the way to go, I've been re-aquainting myself with System.Mem.Weak and now I'm now wondering what is an appropriate key for each ForeignPtr. Would it be OK to use the ForeignPtr itself as it's own key? (Seems OK to me, but this is a bit different from the memoisation example so I thought I'd check.) If so, then I guess the thing to do is to maintain a mutable doubly linked list of Weak pointers to ForeignPtrs using IORef's and have the finaliser for each weak pointer "short out" the corresponding list cell. When the program terminates execute the finalisers of all ForeignPtrs which remain in this list. Hmm, this is getting awfully complicated, and I still have my doubts about it for a couple of reasons.. 1- Executing ForeignPtr finalisers directly (as in Krasimirs example) seems to be ghc specific. 2- If there is no guarantee whether or when ForeignPtr finalisers are run then it seems that it is possible that a Weak pointer finaliser has been run (thereby deleting the weak pointer reference from the list), but the corresponding ForeignPtr finaliser has *not* been run. The solution to problem 2 would seem to be to not associate any finaliser with with the ForeignPtr, but do all finalisation in the Weak pointer finaliser. I guess that would cure problem 1 too. What do folk think about this?
performGC doesn't do anything that you can rely on :-)
Oh, that's handy :-)
Also, I could you explain what you mean by a suitable exception handler? I don't really understand this at all. I'd expected I may well end up using bracket or similar, but I'm not sure how exception handling is relevant to this problem.
Start your program something like this:
import Control.Exception (finally)
main = my_main `finally` clean_up my_main = ... put your program here ... clean_up = ... all the cleanup code goes here ...
You can additionally use finalizers to perform incremental cleanup during program execution, but the right way to clean up at the end is to use an exception handler as above.
Ah OK, I was hoping the whole thing would be something as simple as this.. withLibXYX :: IO () -> IO () withLibXYZ doit = finally (initialiseLibXYZ >> doit) (performGC >> shutdownLibXYZ) Where initialiseLibXYZ and shutdownLibXYZ are simple foreign functions imported from libXYZ. I think it's a real shame performGC or some other similar function can't simply guarantee that all (garbage) ForeignPtr finalisers have been run before calling shutdownLibXYZ :-( Regards -- Adrian Hey

On Tuesday 23 Dec 2003 7:22 am, Adrian Hey wrote:
Assuming the weak pointers solution is the way to go, I've been re-aquainting myself with System.Mem.Weak and now I'm now wondering what is an appropriate key for each ForeignPtr.
Would it be OK to use the ForeignPtr itself as it's own key? (Seems OK to me, but this is a bit different from the memoisation example so I thought I'd check.)
I guess I should've taken a look at mkWeakPtr :: k -> Maybe (IO ()) -> IO (Weak k) before asking this :-) Regards -- Adrian Hey
participants (4)
-
Adrian Hey
-
Carl Witty
-
Simon Marlow
-
Tomasz Zielonka