Search code examples
haskellfrpreactive-banana

In reactive-banana, is it safe to trigger handler actions from multiple threads?


Is it safe to trigger the fire action in

(addHandler, fire) <- newAddHandler

from a different thread from which the reactive-banana graph was compiled?


Solution

  • Yes, this is safe, but there is the caveat that @Cirdec mentioned.

    For conreteness, consider the following example that creates an event network using the addHandler in a separate thread and then calls fire repeatedly in the main thread

    import Control.Concurrent (myThreadId, threadDelay, forkIO)
    
    main = do
        ...
        (addHandler, fire) <- newAddHandler
    
        let networkDescription :: MomentIO ()
            networkDescription = do
               e <- fromAddHandler addHandler
               ...
               reactimate $ (print =<< myThreadId) <$ e   -- reactimate
    
        forkIO $ do
            network <- compile networkDescription
            actuate network
        ...
        forever $ do                                      -- event loop
            threadDelay (10^6)
            fire ()
    

    (See the documentation "Terminating the program" in Control.Concurrent for why I've put the event loop in the main thread as opposed to putting the network in the main thread.)

    In this and similar situations, the following will hold:

    • The IO actions executed by the reactimate will be run in the thread that calls fire, not in the thread where the network was compiled. This is what @Cirdec already mentioned.
    • If there were a second thread also calling fire, then it could potentially interleave with other calls to fire, i.e. the program could be calling fire twice concurrently. Then,
      • Reactive-banana uses a lock to ensure that Behaviors and Events are updated consistently. You can treat them as pure functions Time -> a and lists [(Time,a)] as usual.
      • However, the IO actions from the reactimates may interleave. In other words, the pure FRP part will stay pure, but the actual IO is subject to concurrency as usual.