content: module FRP.Sodium.IO where
  
  import FRP.Sodium.Context
  import FRP.Sodium.Internal
  
  import Control.Concurrent (forkIO)
  
  
  -- | Execute the specified IO operation asynchronously on a separate thread, and
  -- signal the output event in a new transaction upon its completion.
  --
  -- Caveat: Where 'switch' or 'switchE' is used, when some reactive logic has been
  -- switched away, we rely on garbage collection to actually disconnect this logic
  -- from any input it may be listening to. With normal Sodium code, everything is
  -- pure, so before garbage collection happens, the worst we will get is some wasted
  -- CPU cycles. If you are using 'executeAsyncIO'/'executeSyncIO' inside a 'switch'
  -- or 'switchE', however, it is possible that logic that has been switched away
  -- hasn't been garbage collected yet. This logic /could/ still run, and if it has
  -- observable effects, you could see it running after it is supposed to have been
  -- switched out. One way to avoid this is to pipe the source event for IO out of the
  -- switch, run the 'executeAsyncIO'/'executeSyncIO' outside the switch, and pipe its
  -- output back into the switch contents.
  executeAsyncIO :: Event Plain (IO a) -> Event Plain a
  executeAsyncIO ev = Event gl cacheRef (dep ev)
    where
      cacheRef = unsafeNewIORef Nothing ev
      gl = do
          (l, push, nodeRef) <- ioReactive newEventImpl
          unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> do
              ioReactive $ do
                  _ <- forkIO $ sync . push =<< action
                  return ()
          addCleanup_Listen unlistener l
  
  -- | Execute the specified IO operation synchronously and fire the output event
  -- in the same transaction.
  --
  -- Caveat: See 'executeAsyncIO'.
  executeSyncIO :: Event Plain (IO a) -> Event Plain a
  executeSyncIO ev = Event gl cacheRef (dep ev)
    where
      cacheRef = unsafeNewIORef Nothing ev
      gl = do
          (l, push, nodeRef) <- ioReactive newEventImpl
          unlistener <- later $ linkedListen ev (Just nodeRef) False $ \action -> do
              push =<< ioReactive action
          addCleanup_Listen unlistener l
  
  