-
Notifications
You must be signed in to change notification settings - Fork 63
Description
Ouch. I think I found an issue with StablePtr and concurrency.
While working on threepenny-gui again, I find myself in a situation where I need to run an event loop in a thread (that is separate from the main thread) that reads events from a TQueue. Every now and then, the JavaScript side calls apply_sp to write an event to the queue. Thanks to concurrency, the event loop will pick it up at the appropriate time — typically only after the call to apply_sp has finished.
The issue is this: The single occurrence of writeTQueue events is exported to the JavaScript side via newStablePtr. The event loop uses readTQueue events, but the Haskell side thinks that this is a deadlock, because it cannot see that the JavaScript can, in principle, still write to the events :: TQueue.
Long story short: I expect that the following program
import Control.Monad
( join )
import Control.Concurrent.MVar
( MVar, newEmptyMVar, putMVar, takeMVar )
import Control.Concurrent
( forkFinally )
import Foreign.StablePtr
( StablePtr, deRefStablePtr, newStablePtr, castStablePtrToPtr )
main :: IO ()
main = do
putStrLn "Start of main thread"
done <- newEmptyMVar
events <- newEmptyMVar
ptr <- newStablePtr $ putMVar events ()
print $ castStablePtrToPtr ptr
forkFinally (eventLoop events) (\_ -> putMVar done ())
-- putMVar events ()
takeMVar done
eventLoop :: MVar () -> IO ()
eventLoop mvar = do
_ <- takeMVar mvar
putStrLn "End of eventLoop"prints
Start of main thread
0x1
to the console and hangs. However, the actual behavior is that the program prints
Start of main thread
0x1
ERR: deadlock
and exits.
Uncommenting the line -- putMVar events () makes the program finish. I think this means that events is garbage collected, even though it should be protected by the call to newStablePtr. (EDIT: I'm no longer sure about this claim.)
The print $ castStablePtrToPtr ptr statement is just for show, to indicate that a foreign runtime could use the printed value with apply_sp to put something into the events variable.
(EDIT: Sorry, in the previous version, I think I got the expected result wrong. It's expected for the program to hang instead of detecting a deadlock. GHC does the expected thing.)