
When I read the docs for System.Mem.Weak, it all seems to make sense. But then this code doesn't run as I expect it to when I turn on -threaded: http://hpaste.org/63832 (Expected/actual output are listed in the paste.) I've tried this on 7.4.1 and 7.0.4 with the same results. Can someone enlighten me? Cheers, Mike Craig

On Thu, Feb 16, 2012 at 2:04 PM, Michael Craig
When I read the docs for System.Mem.Weak, it all seems to make sense. But then this code doesn't run as I expect it to when I turn on -threaded: http://hpaste.org/63832 (Expected/actual output are listed in the paste.)
I've tried this on 7.4.1 and 7.0.4 with the same results. Can someone enlighten me?
First off, I'm pretty sure finalizers won't run until the data they were associated with has been GCd, and GHC doesn't do GCs unless there is allocation - threadDelay doesn't allocate much, I imagine. Also, from the docs: """ A weak pointer may also have a finalizer of type IO (); if it does, then the finalizer will be run at most once, at a time after the key has become unreachable by the program ("dead"). The storage manager attempts to run the finalizer(s) for an object soon after the object dies, but promptness is not guaranteed. It is not guaranteed that a finalizer will eventually run, and no attempt is made to run outstanding finalizers when the program exits. Therefore finalizers should not be relied on to clean up resources - other methods (eg. exception handlers) should be employed, possibly in addition to finalisers. """ Antoine

Quoting Antoine Latter
On Thu, Feb 16, 2012 at 2:04 PM, Michael Craig
wrote: When I read the docs for System.Mem.Weak, it all seems to make sense. But then this code doesn't run as I expect it to when I turn on -threaded: http://hpaste.org/63832 (Expected/actual output are listed in the paste.)
I've tried this on 7.4.1 and 7.0.4 with the same results. Can someone enlighten me?
First off, I'm pretty sure finalizers won't run until the data they were associated with has been GCd, and GHC doesn't do GCs unless there is allocation - threadDelay doesn't allocate much, I imagine.
This seems to be an explanation of why a finalizer might run later than expected (or not run at all). But his paste shows that it runs *earlier* than what he expected. ~d

64-bit GHC on OS X gives me this:
$ ghc -fforce-recomp -threaded finalizer
[1 of 1] Compiling Main ( finalizer.hs, finalizer.o )
Linking finalizer ...
$ ./finalizer
waiting ...
done!
waiting ...
running finalizer
done!
However, it's a different story when `-O2` is specified:
$ ghc -O2 -fforce-recomp -threaded finalizer
[1 of 1] Compiling Main ( finalizer.hs, finalizer.o )
Linking finalizer ...
$ ./finalizer
waiting ...
running finalizer
done!
waiting ...
done!
This smells like a bug. The stranger thing is that the GC will run the
finalizer, but it doesn't reclaim the object? I'd think `readIORef`
going after an invalidated pointer the GC reclaimed would almost
certainly crash.
On Thu, Feb 16, 2012 at 2:04 PM, Michael Craig
When I read the docs for System.Mem.Weak, it all seems to make sense. But then this code doesn't run as I expect it to when I turn on -threaded: http://hpaste.org/63832 (Expected/actual output are listed in the paste.)
I've tried this on 7.4.1 and 7.0.4 with the same results. Can someone enlighten me?
Cheers, Mike Craig
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards, Austin

This seems to be an explanation of why a finalizer might run later than expected (or not run at all). But his paste shows that it runs *earlier* than what he expected.
What he said.
64-bit GHC on OS X gives me this:
...
However, it's a different story when `-O2` is specified:
...
You're right. I was compiling with cabal and had -O turned on without
knowing it. So this looks like an optimization bug?
Mike Craig
On Thu, Feb 16, 2012 at 3:55 PM, Austin Seipp
64-bit GHC on OS X gives me this:
$ ghc -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... done! waiting ... running finalizer done!
However, it's a different story when `-O2` is specified:
$ ghc -O2 -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... running finalizer done! waiting ... done!
This smells like a bug. The stranger thing is that the GC will run the finalizer, but it doesn't reclaim the object? I'd think `readIORef` going after an invalidated pointer the GC reclaimed would almost certainly crash.
On Thu, Feb 16, 2012 at 2:04 PM, Michael Craig
wrote: When I read the docs for System.Mem.Weak, it all seems to make sense. But then this code doesn't run as I expect it to when I turn on -threaded: http://hpaste.org/63832 (Expected/actual output are listed in the paste.)
I've tried this on 7.4.1 and 7.0.4 with the same results. Can someone enlighten me?
Cheers, Mike Craig
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards, Austin

On Thu, Feb 16, 2012 at 02:55:13PM -0600, Austin Seipp wrote:
64-bit GHC on OS X gives me this:
$ ghc -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... done! waiting ... running finalizer done!
However, it's a different story when `-O2` is specified:
$ ghc -O2 -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... running finalizer done! waiting ... done!
This smells like a bug. The stranger thing is that the GC will run the finalizer, but it doesn't reclaim the object? I'd think `readIORef` going after an invalidated pointer the GC reclaimed would almost certainly crash.
The finalizer is attached to the Thing, not the IORef. I haven't checked, but I assume that ioref gets inlined, so effectively (ioref x) is evaluated early. If you change it to readIORef (ioref' x) >>= \ix -> ix `seq` return () and define {-# NOINLINE ioref' #-} ioref' :: Thing -> IORef Bool ioref' = ioref then you'll get the sort of output you expect. Finalizers are tricky things, especially when combined with some of GHC's optimisations. Thanks Ian

I haven't checked, but ...
I checked, and your solution works. In the context of a larger program, getting NOLINE pragmas in all the right places would be challenging, wouldn't it? I found a bug report on the GHC Trac [1] in which Simon explains the importance of evaluating the thunk before calling addFinalizer. (Otherwise the finalizer is added to the thunk.) This works: newThing :: IO Thing newThing = do x <- Thing `fmap` newIORef True return $ unsafePerformIO ( do x' <- evaluate x addFinalizer x' $ putStrLn "running finalizer" ) `seq` x If anyone can show me how to get rid of unsafePerformIO in there, that'd be great. Tried a few things to no avail.
Finalizers are tricky things, especially when combined with some of GHC's optimisations.
No kidding!
[1] http://hackage.haskell.org/trac/ghc/ticket/5365
Mike Craig
On Thu, Feb 16, 2012 at 4:15 PM, Ian Lynagh
On Thu, Feb 16, 2012 at 02:55:13PM -0600, Austin Seipp wrote:
64-bit GHC on OS X gives me this:
$ ghc -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... done! waiting ... running finalizer done!
However, it's a different story when `-O2` is specified:
$ ghc -O2 -fforce-recomp -threaded finalizer [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) Linking finalizer ... $ ./finalizer waiting ... running finalizer done! waiting ... done!
This smells like a bug. The stranger thing is that the GC will run the finalizer, but it doesn't reclaim the object? I'd think `readIORef` going after an invalidated pointer the GC reclaimed would almost certainly crash.
The finalizer is attached to the Thing, not the IORef. I haven't checked, but I assume that ioref gets inlined, so effectively (ioref x) is evaluated early. If you change it to
readIORef (ioref' x) >>= \ix -> ix `seq` return ()
and define
{-# NOINLINE ioref' #-} ioref' :: Thing -> IORef Bool ioref' = ioref
then you'll get the sort of output you expect.
Finalizers are tricky things, especially when combined with some of GHC's optimisations.
Thanks Ian
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On 16/02/2012 22:20, Michael Craig wrote:
I haven't checked, but ...
I checked, and your solution works. In the context of a larger program, getting NOLINE pragmas in all the right places would be challenging, wouldn't it?
I found a bug report on the GHC Trac [1] in which Simon explains the importance of evaluating the thunk before calling addFinalizer. (Otherwise the finalizer is added to the thunk.) This works:
newThing :: IO Thing newThing = do x <- Thing `fmap` newIORef True return $ unsafePerformIO ( do x' <- evaluate x addFinalizer x' $ putStrLn "running finalizer" ) `seq` x
If anyone can show me how to get rid of unsafePerformIO in there, that'd be great. Tried a few things to no avail.
If your goal is to add a finalizer to an IORef, I would use Data.IORef.mkWeakIORef. It adds the finalizer to the actual primitive MutVar# object inside the IORef, so it's a lot more predictable (the primitive object can't be copied under your feet).
Finalizers are tricky things, especially when combined with some of GHC's optimisations.
No kidding!
Finalizers on ordinary Haskell objects are very limited in usefulness, the only good use I've found is for memo tables. Cheers, Simon
[1] http://hackage.haskell.org/trac/ghc/ticket/5365
Mike Craig
On Thu, Feb 16, 2012 at 4:15 PM, Ian Lynagh
mailto:igloo@earth.li> wrote: On Thu, Feb 16, 2012 at 02:55:13PM -0600, Austin Seipp wrote: > 64-bit GHC on OS X gives me this: > > $ ghc -fforce-recomp -threaded finalizer > [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) > Linking finalizer ... > $ ./finalizer > waiting ... > done! > waiting ... > running finalizer > done! > > However, it's a different story when `-O2` is specified: > > $ ghc -O2 -fforce-recomp -threaded finalizer > [1 of 1] Compiling Main ( finalizer.hs, finalizer.o ) > Linking finalizer ... > $ ./finalizer > waiting ... > running finalizer > done! > waiting ... > done! > > This smells like a bug. The stranger thing is that the GC will run the > finalizer, but it doesn't reclaim the object? I'd think `readIORef` > going after an invalidated pointer the GC reclaimed would almost > certainly crash.
The finalizer is attached to the Thing, not the IORef. I haven't checked, but I assume that ioref gets inlined, so effectively (ioref x) is evaluated early. If you change it to
readIORef (ioref' x) >>= \ix -> ix `seq` return ()
and define
{-# NOINLINE ioref' #-} ioref' :: Thing -> IORef Bool ioref' = ioref
then you'll get the sort of output you expect.
Finalizers are tricky things, especially when combined with some of GHC's optimisations.
Thanks Ian
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org mailto:Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
participants (6)
-
Antoine Latter
-
Austin Seipp
-
Ian Lynagh
-
Michael Craig
-
Simon Marlow
-
wagnerdm@seas.upenn.edu