
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