reactive-banana icon indicating copy to clipboard operation
reactive-banana copied to clipboard

Event handlers should not be called inside `execute`

Open mitchellwrosen opened this issue 5 years ago • 3 comments

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.

mitchellwrosen avatar Sep 11 '18 16:09 mitchellwrosen

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.)

HeinrichApfelmus avatar May 11 '19 11:05 HeinrichApfelmus

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.

ocharles avatar Jan 08 '22 19:01 ocharles