module Driver ( Transition , DriverState , applyTransition , initDriverState , takeState ) where import When import Control.Concurrent import Control.Concurrent.MVar import Data.Maybe (catMaybes) import Data.Time.Clock import Data.Time.Format() import qualified Data.Map as M -------------- type Transition st a = st -> ([a], [Timed a], UTCTime -> IO (), st) newtype DriverState st a = DriverState (MVar (M.Map a ThreadId, st)) ----------------- initDriverState :: st -> IO (DriverState st a) initDriverState st = do state <- newMVar (M.empty, st) return $ DriverState state takeState :: DriverState st a -> IO ([a], st) takeState (DriverState dst) = do (ma, st) <- takeMVar dst return (M.keys ma, st) applyTransition :: (Eq a, Ord a, Show a) => DriverState st a -> (a -> Transition st a) -> Transition st a -> IO () applyTransition (DriverState dst) action transition = appTransition Nothing transition where appTransition this_action tr = do time <- getCurrentTime res <- modifyMVar dst $ \(am, st) -> do let (cs, as, res, new_st) = tr st let am' = maybe am (`M.delete` am) this_action mapM_ killThread $ catMaybes $ map (`M.lookup` am') $ cs ++ map snd as let am'' = foldl (flip M.delete) am' cs tids <- mapM (forkIO . initAction time) as let am''' = M.fromList (zip (map snd as) tids) `M.union` am'' return ((am''', new_st), res) res time initAction time (when, a) = do case when of Now -> return () AfterEval x -> x `seq` return () At t -> waitTill t Later dt -> waitTill (addUTCTime dt time) appTransition (Just a) (action a) waitTill time = do time' <- getCurrentTime case round $ 1000000 * (time `diffUTCTime` time') of n | n > 0 -> threadDelay n _ -> return ()