module Scheduler.Internal ( SchedulerIO(..)
, getCurrentScheduler
, Scheduler(..)
, BackgroundScheduler(..)
, ScheduledAction
, newScheduledAction
, executeScheduledAction
) where
import Control.Applicative
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.IO.Class
import Data.Functor
import Data.IORef
import Disposable
data SchedulerIO s a where
SchedulerIO :: Scheduler s => (s -> IO a) -> SchedulerIO s a
getCurrentScheduler :: Scheduler s => SchedulerIO s s
getCurrentScheduler = SchedulerIO return
instance Functor (SchedulerIO s) where
fmap f (SchedulerIO mf) = SchedulerIO $ fmap f . mf
instance Scheduler s => Monad (SchedulerIO s) where
return v = SchedulerIO $ \_ -> return v
(SchedulerIO mf) >>= f =
SchedulerIO $ \sch -> do
v <- mf sch
let unwrap sch (SchedulerIO mf') = mf' sch
unwrap sch $ f v
instance Scheduler s => MonadIO (SchedulerIO s) where
liftIO action = SchedulerIO $ \_ -> action
instance Scheduler s => Applicative (SchedulerIO s) where
pure = return
(<*>) = ap
data ScheduledAction s where
ScheduledAction :: Scheduler s => IORef Bool -> SchedulerIO s () -> ScheduledAction s
newScheduledAction :: Scheduler s => SchedulerIO s () -> IO (ScheduledAction s, Disposable)
newScheduledAction action = do
ref <- newIORef False
d <- newDisposable $ atomicModifyIORef ref $ const (True, ())
return (ScheduledAction ref action, d)
class Scheduler s where
schedule :: s -> SchedulerIO s () -> IO Disposable
schedulerMain :: s -> IO ()
newtype BackgroundScheduler = BackgroundScheduler (TQueue (ScheduledAction BackgroundScheduler))
instance Scheduler BackgroundScheduler where
schedule s@(BackgroundScheduler q) action = do
(sa, d) <- newScheduledAction action
let schedule' = do
e <- isEmptyTQueue q
writeTQueue q sa
return e
e <- atomically schedule'
when e $ void $ forkIO $ schedulerMain s
return d
schedulerMain s@(BackgroundScheduler q) = do
m <- atomically $ tryReadTQueue q
maybe (return ()) (executeScheduledAction s) m
executeScheduledAction :: Scheduler s => s -> ScheduledAction s -> IO ()
executeScheduledAction s (ScheduledAction ref (SchedulerIO mf)) = do
d <- readIORef ref
unless d $ mf s
yield
schedulerMain s