reactive-banana
reactive-banana copied to clipboard
Event handlers should not be called inside `execute`
If I'm not mistaken, the MomentIO
action carried by an Event
that is passed to execute
...
execute :: Event (MomentIO a) -> MomentIO (Event a)
^^^^^^^^^^
...should never call an event handler, as this will eagerly try to re-enter the network whose lock is currently held, then fail with an MVar operation exception.
Does that sound correct?
Here's a sample program that exhibits this behavior.
import Data.Function
import Reactive.Banana.Frameworks
import System.IO
main :: IO ()
main = do
hSetEcho stdout False
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
(ah, fire) <- newAddHandler
network <- compile $ do
-- Print character presses
e <- fromAddHandler ah
reactimate ((\c -> print ("e: " ++ show c)) <$> e)
-- Create a new `Event Char` and print its occurrences as well
(e2, fire2) <- newEvent
reactimate ((\c -> print ("e2: " ++ show (c :: Char))) <$> e2)
-- Create a new event that, when `e` fires, calls `fire2`, which causes `e2` to fire.
e3 <- execute (liftIO . fire2 <$> e)
reactimate ((\c -> print ("e3: " ++ show c)) <$> e3)
actuate network
-- Fire character presses into the network. Press 'q' to quit.
fix $ \loop -> do
c <- getChar
case c of
'q' -> pure ()
_ -> fire c >> loop
And the output when a key is pressed:
thread blocked indefinitely in an MVar operation
If this is the case, some more documentation on execute
would be helpful, w/ example uses and pitfalls. I've been using reactive-banana for a while and I don't think I've ever fully understood exactly how the timing of execute
works, or what it's useful for - probably still don't. Thanks, cheers.
Yes, this is the case. The primary purpose of execute
is to allow setting up new event handlers and reactimate
while an event happens. (This is what the documentation "Dynamically add input and output to an existing event network." alludes to.)
I wonder if we can also do a better job about
thread blocked indefinitely in an MVar operation
Maybe using tryTakeMVar
and throwing a more informative exception on the Nothing
case would do the job.