{-# LANGUAGE LambdaCase #-} module Control.FRPNow.Private.PrimEv(Round, Clock, PrimEv, newClock , callbackp, spawn, spawnOS, curRound, newRound ,observeAt ) where import Control.Applicative import System.IO.Unsafe import Data.IORef import Data.Unique import Control.Concurrent import Debug.Trace data Clock = Clock { identClock :: Unique, scheduleRound :: IO (), roundRef :: IORef Integer, changedRef :: IORef Bool } data Round = Round Unique Integer data PrimEv a = PrimEv Unique (IORef (Maybe (Round, a))) instance Show Round where show (Round _ i) = show i -- when given a IO action that schedules a round, create a new clock newClock :: IO () -> IO Clock newClock schedule = Clock <$> newUnique <*> pure schedule <*> newIORef 0 <*> newIORef False callbackp :: Clock -> IO (PrimEv a, a -> IO ()) callbackp c = do mv <- newIORef Nothing return (PrimEv (identClock c) mv, setValue mv) where setValue mv x = do i <- readIORef (roundRef c) v <- readIORef mv case v of Just _ -> error "Already called callback!" _ -> return () writeIORef mv (Just (Round (identClock c) (i + 1), x)) writeIORef (changedRef c) True scheduleRound c spawn :: Clock -> IO a -> IO (PrimEv a) spawn c m = do (pe,setVal) <- callbackp c forkIO $ m >>= setVal return pe spawnOS :: Clock -> IO a -> IO (PrimEv a) spawnOS c m = do (pe,setVal) <- callbackp c forkOS $ m >>= setVal return pe curRound :: Clock -> IO Round curRound c = Round (identClock c) <$> readIORef (roundRef c) newRound :: Clock -> IO Bool newRound c = readIORef (changedRef c) >>= \change -> if change then do writeIORef (changedRef c) False modifyIORef (roundRef c) (+1) return True else return False observeAt :: PrimEv a -> Round -> Maybe a observeAt (PrimEv uv m) (Round ur t) | uv /= ur = error "Observation of TIVar from another context!" | otherwise = unsafePerformIO $ do v <- readIORef m return $ case v of Just (Round _ t',a) | t' <= t -> Just a _ -> Nothing instance Eq Round where (Round lu lt) == (Round ru rt) | lu == ru = lt == rt | otherwise = error "Rounds not from same clock!" instance Ord Round where compare (Round lu lt) (Round ru rt) | lu == ru = compare lt rt | otherwise = error "Rounds not from same clock!"