In the following code I am sending a record with an IO (TMVar o) off to get populated in a function then reading
what I believe should be the same TMVar when the function returns. The problem is when I read it back it's empty and the application just blocks.
Why is this happening?
The Hook is intialised as follows:
hook = Hook {
-- other props
hookResult = newEmptyTMVarIO,
}
later in the ap:
executeHook :: (Text -> IO ()) -> Node i o -> IO ()
executeHook db =
\case
Fixture {} -> pure ()
Hook
{ hookParent,
hookStatus,
hook,
hookResult, -- IO (TMVar o)
hookChildren
} -> do
input <- db "CALL PARENT LOCK EXECUTE HOOK" >> lockExecuteHook db hookParent
result <- hook input
hr <- hookResult
mtb <- atomically $ isEmptyTMVar hr
db $ "HOOK RESULT PUT EMPTY BEFORE: " <> txt mtb
atomically $ putTMVar hr result -- writes hook result to the TMVar
mt <- atomically $ isEmptyTMVar hr
db $ "HOOK RESULT PUT EMPTY AFTER: " <> txt mt
lockExecuteHook :: (Text -> IO ()) -> Either o (Node i o) -> IO o
lockExecuteHook db parent =
eitherf
parent
(\o -> db "NO PARENT HOOK RETURNING VALUE" >> pure o)
( \case
Fixture {} -> pure ()
hk@Hook
{ hookParent,
hookStatus,
hookResult, -- IO (TMVar o)
hook,
hookChildren,
hookRelease
} -> do
bs <- hookStatus
wantLaunch <- atomically $ tryLock db bs
db $ "HOOK LOCK >>> " <> txt wantLaunch
when
wantLaunch
$ executeHook db hk -- this writes hook result to the TMVar
hr <- hookResult
mt <- atomically $ isEmptyTMVar hr
db $ "READING HOOK !!!!!!!!!!!!!!!!!!!!!!!!!! EMPTY: " <> txt mt
r <- atomically $ readTMVar hr
db "RETURNING FROM LOCK EXECUTE HOOK " >> pure r
)
Debug (db) output
CALL PARENT LOCK EXECUTE HOOK
HOOK LOCK >>> True
HOOK RESULT PUT EMPTY BEFORE: True
HOOK RESULT PUT EMPTY AFTER: False
READING HOOK !!!!!!!!!!!!!!!!!!!!!!!!!! EMPTY: True
As pointed out in the above comments by @FyodorSoikin, @DanielWagner and @chi, the reason this was not working as expected is that hookResult was not initialised correctly.
Setting the record field to an IO (TMVar a) will result in a new TMVar a every time hookResult is read, similar problem would occur if the field was set to an STM (TMVar a) using newTMVar. To make this work hookResult needs to be set to a TMVar a from within the IO or STM context.
eg :: IO (Hook a)
eg = do
v <- newEmptyTMVarIO
pure $ Hook {
-- other props
hookResult = v,
}