Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#5365 closed bug (invalid)

Finalizer running prematurely in ghci

Reported by: ekmett Owned by: simonmar
Priority: normal Milestone:
Component: GHCi Version: 7.0.3
Keywords: finalizers Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

I have been building a library for doing hash consing, but it seemed to incorrectly finalize references to live entries in the hash cons table when I use it from ghci.

I've distilled the problem to the following test case.

Prelude System.Mem.Weak GHC.IO> let x = let y = "foo" in (unsafePerformIO $ addFinalizer y (putStrLn "deleted")) `seq` y
Prelude System.Mem.Weak GHC.IO> x
"foo"
deleted
Prelude System.Mem.Weak GHC.IO> x
"foo"

Change History (4)

comment:1 Changed 3 years ago by ekmett

I was able to work around this by seq'ing y before adding a finalizer.

Prelude System.Mem.Weak GHC.IO> let x =

let y = "foo" in y seq (unsafePerformIO $ addFinalizer y (putStrLn $ "deleted")) seq y

Prelude System.Mem.Weak GHC.IO> x
"foo"
Prelude System.Mem.Weak GHC.IO> x
"foo"

So it appears that the finalizer was attached to the thunk that computes y, and when y was evaluated, I no longer had any reference to the original thunk.

comment:2 Changed 3 years ago by simonpj

  • Owner set to simonmar

comment:3 Changed 3 years ago by simonmar

  • Resolution set to invalid
  • Status changed from new to closed

You are attaching a finalizer to y, which is a thunk. After y is evaluated, the thunk is no longer referenced, so the finalizer runs. (y might not look like a thunk, but it is in fact a call to unpackCString#).

Usually you want to seq before calling addFinalizer for this reason, although note that seq doesn't guarantee ordering so you could still get unexpected results. This is probably better:

let y = "foo" in (unsafePerformIO $ do
                    y' <- evaluate y
                    addFinalizer y' (putStrLn $ "deleted")) `seq` y 

comment:4 Changed 3 years ago by ezyang

I'd like to remark that it is not at all clear from the documentation at System.Mem.Weak that there is no continuity of objects between thunks and evaluated values. I was going to also give an example of how the behavior was different if you just did a normal Haskell file, but I couldn't get the finalizer to run at all in that case. Oops.

Note: See TracTickets for help on using tickets.