freckle-app-1.10.5.0: library/Freckle/App/Async.hs
module Freckle.App.Async
( async
, immortalCreate
, immortalCreateLogged
)
where
import Freckle.App.Prelude
import Blammo.Logging
import qualified Control.Immortal as Immortal
import Control.Monad (forever)
import qualified Data.Aeson.Compat as KeyMap
import UnliftIO.Async (Async)
import qualified UnliftIO.Async as UnliftIO
import UnliftIO.Concurrent (threadDelay)
-- | 'UnliftIO.Async.async' but passing the thread context along
async :: (MonadMask m, MonadUnliftIO m) => m a -> m (Async a)
async f = do
tc <- liftIO $ KeyMap.toList <$> myThreadContext
UnliftIO.async $ withThreadContext tc f
-- | Wrapper around creating "Control.Immortal" processes
--
-- Features:
--
-- - Ensures the thread context is correctly passed to both your spawned action
-- and your error handler
-- - Blocks forever after spawning your thread.
immortalCreate
:: (MonadMask m, MonadUnliftIO m)
=> (Either SomeException () -> m ())
-- ^ How to handle unexpected finish
-> m ()
-- ^ The action to run persistently
-> m a
immortalCreate onUnexpected act = do
tc <- liftIO $ KeyMap.toList <$> myThreadContext
let
act' = withThreadContext tc act
onUnexpected' = withThreadContext tc . onUnexpected
void $ Immortal.create $ \thread -> do
Immortal.onUnexpectedFinish thread onUnexpected' act'
forever $ threadDelay maxBound
-- | 'immortalCreate' with logging of unexpected finishes
immortalCreateLogged
:: (MonadMask m, MonadUnliftIO m, MonadLogger m) => m () -> m a
immortalCreateLogged = immortalCreate $ either logEx pure
where
logEx ex = logError $ "Unexpected Finish" :# ["exception" .= displayException ex]