{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies     #-}
module FRP.Rhine.Schedule.Concurrently where

-- base
import Control.Concurrent

-- rhine
import FRP.Rhine


-- | Runs two clocks in separate GHC threads
--   and collects the results in the foreground thread.
--   Caution: The data processing will still happen in the same thread
--   (since data processing and scheduling are separated concerns).
concurrently :: (Clock IO cl1, Clock IO cl2, TimeDomainOf cl1 ~ TimeDomainOf cl2) => Schedule IO cl1 cl2
concurrently =  Schedule $ \cl1 cl2 -> do
  iMVar <- newEmptyMVar
  mvar  <- newEmptyMVar
  _ <- forkIO $ do
    (runningClock, initTime) <- startClock cl1
    putMVar iMVar initTime
    reactimate $ runningClock >>> second (arr Left)  >>> arrM (putMVar mvar)
  _ <- forkIO $ do
    (runningClock, initTime) <- startClock cl2
    putMVar iMVar initTime
    reactimate $ runningClock >>> second (arr Right) >>> arrM (putMVar mvar)
  initTime <- takeMVar iMVar -- The first clock to be initialised sets the first time stamp
  _        <- takeMVar iMVar -- Initialise the second clock
  return (arrM_ $ takeMVar mvar, initTime)

-- TODO These threads can't be killed from outside easily since we've lost their ids
-- => make a MaybeT or ExceptT variant