module TheatreDev.Daemon
( Daemon,
Config (..),
spawn,
kill,
wait,
)
where
import TheatreDev.Prelude
import TheatreDev.Wait qualified as Wait
data Config = forall state.
Config
{
()
initialState :: state,
()
iterate :: state -> IO state,
()
cleanUp :: state -> IO ()
}
data Daemon = Daemon
{
Daemon -> STM ()
kill :: STM (),
Daemon -> STM (Maybe SomeException)
wait :: STM (Maybe SomeException)
}
instance Semigroup Daemon where
Daemon
left <> :: Daemon -> Daemon -> Daemon
<> Daemon
right =
Daemon
{ $sel:kill:Daemon :: STM ()
kill = Daemon
left.kill forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Daemon
right.kill,
$sel:wait:Daemon :: STM (Maybe SomeException)
wait = STM (Maybe SomeException)
-> STM (Maybe SomeException) -> STM (Maybe SomeException)
Wait.both Daemon
left.wait Daemon
right.wait
}
instance Monoid Daemon where
mempty :: Daemon
mempty =
Daemon
{ $sel:kill:Daemon :: STM ()
kill = forall (m :: * -> *) a. Monad m => a -> m a
return (),
$sel:wait:Daemon :: STM (Maybe SomeException)
wait = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
}
mconcat :: [Daemon] -> Daemon
mconcat [Daemon]
daemons =
Daemon
{ $sel:kill:Daemon :: STM ()
kill = forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (.kill) [Daemon]
daemons,
$sel:wait:Daemon :: STM (Maybe SomeException)
wait = [STM (Maybe SomeException)] -> STM (Maybe SomeException)
Wait.all (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (.wait) [Daemon]
daemons)
}
spawn :: Config -> IO Daemon
spawn :: Config -> IO Daemon
spawn Config {state
state -> IO state
state -> IO ()
cleanUp :: state -> IO ()
iterate :: state -> IO state
initialState :: state
$sel:cleanUp:Config :: ()
$sel:iterate:Config :: ()
$sel:initialState:Config :: ()
..} = do
TVar Bool
iteratingVar <- forall a. a -> IO (TVar a)
newTVarIO Bool
True
TMVar (Maybe SomeException)
resultVar <- forall a. IO (TMVar a)
newEmptyTMVarIO
((forall a. IO a -> IO a) -> IO ()) -> IO ThreadId
forkIOWithUnmask forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask ->
let go :: state -> IO ()
go !state
state = do
Bool
iterating <- forall a. TVar a -> IO a
readTVarIO TVar Bool
iteratingVar
if Bool
iterating
then do
Either SomeException state
iterationAttemptResult <- forall e a. Exception e => IO a -> IO (Either e a)
try (forall a. IO a -> IO a
unmask (state -> IO state
iterate state
state))
case Either SomeException state
iterationAttemptResult of
Right state
newState -> state -> IO ()
go state
newState
Left SomeException
exception -> do
forall e a. Exception e => IO a -> IO (Either e a)
try @SomeException (forall a. IO a -> IO a
unmask (state -> IO ()
cleanUp state
state))
forall a. STM a -> IO a
atomically (forall a. TMVar a -> a -> STM ()
putTMVar TMVar (Maybe SomeException)
resultVar (forall a. a -> Maybe a
Just SomeException
exception))
else do
Either SomeException ()
cleanUpResult <- forall e a. Exception e => IO a -> IO (Either e a)
try @SomeException (forall a. IO a -> IO a
unmask (state -> IO ()
cleanUp state
state))
case Either SomeException ()
cleanUpResult of
Right () -> forall a. STM a -> IO a
atomically (forall a. TMVar a -> a -> STM ()
putTMVar TMVar (Maybe SomeException)
resultVar forall a. Maybe a
Nothing)
Left SomeException
exception -> forall a. STM a -> IO a
atomically (forall a. TMVar a -> a -> STM ()
putTMVar TMVar (Maybe SomeException)
resultVar (forall a. a -> Maybe a
Just SomeException
exception))
in state -> IO ()
go state
initialState
return
Daemon
{ $sel:kill:Daemon :: STM ()
kill = forall a. TVar a -> a -> STM ()
writeTVar TVar Bool
iteratingVar Bool
False,
$sel:wait:Daemon :: STM (Maybe SomeException)
wait = forall a. TMVar a -> STM a
readTMVar TMVar (Maybe SomeException)
resultVar
}
where
kill :: Daemon -> IO ()
kill :: Daemon -> IO ()
kill Daemon
daemon =
forall a. STM a -> IO a
atomically Daemon
daemon.kill
wait :: Daemon -> IO ()
wait :: Daemon -> IO ()
wait Daemon
daemon =
forall a. STM a -> IO a
atomically Daemon
daemon.wait forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) forall e a. Exception e => e -> IO a
throwIO