module UnliftIO.AutoUpdate.Thread
(
mkAutoUpdate
, mkAutoUpdateWithModify
)
where
import Control.Exception (throw)
import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO (..))
import Data.Maybe (fromMaybe)
import GHC.Conc.Sync (labelThread)
import UnliftIO (MonadUnliftIO)
import UnliftIO.AutoUpdate.Types
import UnliftIO.Concurrent (forkIO, threadDelay)
import UnliftIO.Exception
( SomeException
, catch
, mask_
, try
)
import UnliftIO.IORef (newIORef, readIORef, writeIORef)
import UnliftIO.MVar
( newEmptyMVar
, putMVar
, readMVar
, takeMVar
, tryPutMVar
)
mkAutoUpdate :: MonadUnliftIO m => UpdateSettings m a -> m (m a)
mkAutoUpdate :: forall (m :: * -> *) a.
MonadUnliftIO m =>
UpdateSettings m a -> m (m a)
mkAutoUpdate UpdateSettings m a
us = forall (m :: * -> *) a.
MonadUnliftIO m =>
UpdateSettings m a -> Maybe (a -> m a) -> m (m a)
mkAutoUpdateHelper UpdateSettings m a
us forall a. Maybe a
Nothing
mkAutoUpdateWithModify :: MonadUnliftIO m => UpdateSettings m a -> (a -> m a) -> m (m a)
mkAutoUpdateWithModify :: forall (m :: * -> *) a.
MonadUnliftIO m =>
UpdateSettings m a -> (a -> m a) -> m (m a)
mkAutoUpdateWithModify UpdateSettings m a
us a -> m a
f = forall (m :: * -> *) a.
MonadUnliftIO m =>
UpdateSettings m a -> Maybe (a -> m a) -> m (m a)
mkAutoUpdateHelper UpdateSettings m a
us (forall a. a -> Maybe a
Just a -> m a
f)
mkAutoUpdateHelper :: MonadUnliftIO m => UpdateSettings m a -> Maybe (a -> m a) -> m (m a)
mkAutoUpdateHelper :: forall (m :: * -> *) a.
MonadUnliftIO m =>
UpdateSettings m a -> Maybe (a -> m a) -> m (m a)
mkAutoUpdateHelper UpdateSettings m a
us Maybe (a -> m a)
updateActionModify = do
MVar ()
needsRunning <- forall (m :: * -> *) a. MonadIO m => m (MVar a)
newEmptyMVar
MVar a
responseVar0 <- forall (m :: * -> *) a. MonadIO m => m (MVar a)
newEmptyMVar
IORef (Either (MVar a) a)
currRef <- forall (m :: * -> *) a. MonadIO m => a -> m (IORef a)
newIORef forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left MVar a
responseVar0
let fillRefOnExit :: m () -> m ()
fillRefOnExit m ()
f = do
Either SomeException ()
eres <- forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> m (Either e a)
try m ()
f
case Either SomeException ()
eres of
Left SomeException
e ->
forall (m :: * -> *) a. MonadIO m => IORef a -> a -> m ()
writeIORef IORef (Either (MVar a) a)
currRef forall a b. (a -> b) -> a -> b
$
forall a. HasCallStack => [Char] -> a
error forall a b. (a -> b) -> a -> b
$
[Char]
"Control.AutoUpdate.mkAutoUpdate: worker thread exited with exception: "
forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show (SomeException
e :: SomeException)
Right () ->
forall (m :: * -> *) a. MonadIO m => IORef a -> a -> m ()
writeIORef IORef (Either (MVar a) a)
currRef forall a b. (a -> b) -> a -> b
$
forall a. HasCallStack => [Char] -> a
error forall a b. (a -> b) -> a -> b
$
[Char]
"Control.AutoUpdate.mkAutoUpdate: worker thread exited normally, "
forall a. [a] -> [a] -> [a]
++ [Char]
"which should be impossible due to usage of infinite loop"
ThreadId
tid <- forall (m :: * -> *) a. MonadUnliftIO m => m a -> m a
mask_ forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). MonadUnliftIO m => m () -> m ThreadId
forkIO forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *}. MonadUnliftIO m => m () -> m ()
fillRefOnExit forall a b. (a -> b) -> a -> b
$ do
let loop :: MVar a -> Maybe a -> m b
loop MVar a
responseVar Maybe a
maybea = do
forall (m :: * -> *) a. MonadIO m => MVar a -> m a
takeMVar MVar ()
needsRunning
a
a <- forall (m :: * -> *) a. MonadUnliftIO m => m a -> m a
catchSome forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a -> a
fromMaybe (forall (m :: * -> *) a. UpdateSettings m a -> m a
updateAction UpdateSettings m a
us) (Maybe (a -> m a)
updateActionModify forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe a
maybea)
forall (m :: * -> *) a. MonadIO m => IORef a -> a -> m ()
writeIORef IORef (Either (MVar a) a)
currRef forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right a
a
forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m ()
putMVar MVar a
responseVar a
a
forall (m :: * -> *). MonadIO m => Int -> m ()
threadDelay forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. UpdateSettings m a -> Int
updateFreq UpdateSettings m a
us
MVar a
responseVar' <- forall (m :: * -> *) a. MonadIO m => m (MVar a)
newEmptyMVar
forall (m :: * -> *) a. MonadIO m => IORef a -> a -> m ()
writeIORef IORef (Either (MVar a) a)
currRef forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left MVar a
responseVar'
MVar a -> Maybe a -> m b
loop MVar a
responseVar' (forall a. a -> Maybe a
Just a
a)
forall {b}. MVar a -> Maybe a -> m b
loop MVar a
responseVar0 forall a. Maybe a
Nothing
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. ThreadId -> [Char] -> IO ()
labelThread ThreadId
tid forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. UpdateSettings m a -> [Char]
updateThreadName UpdateSettings m a
us
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ do
Either (MVar a) a
mval <- forall (m :: * -> *) a. MonadIO m => IORef a -> m a
readIORef IORef (Either (MVar a) a)
currRef
case Either (MVar a) a
mval of
Left MVar a
responseVar -> do
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadIO m => MVar a -> a -> m Bool
tryPutMVar MVar ()
needsRunning ()
forall (m :: * -> *) a. MonadIO m => MVar a -> m a
readMVar MVar a
responseVar
Right a
val -> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
val
catchSome :: MonadUnliftIO m => m a -> m a
catchSome :: forall (m :: * -> *) a. MonadUnliftIO m => m a -> m a
catchSome m a
act = forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> (e -> m a) -> m a
UnliftIO.Exception.catch m a
act forall a b. (a -> b) -> a -> b
$ \SomeException
e -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a e. Exception e => e -> a
throw (SomeException
e :: SomeException)