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,
}