{-# LANGUAGE TemplateHaskell #-}
{- |
  This module holds `forkC`, because we use it in at  least two other modules.
-}
module Network.Legion.Fork (
  forkC
) where

import Control.Concurrent (forkIO)
import Control.Exception (SomeException, try)
import Control.Monad (void)
import Control.Monad.Logger (logError, askLoggerIO, runLoggingT)
import Control.Monad.Trans.Class (lift)
import Data.Text (pack)
import Network.Legion.LIO (LIO)
import System.Exit (ExitCode(ExitFailure))
import System.IO (hPutStrLn, stderr)
import System.Posix.Process (exitImmediately)

{- |
  Forks a critical thread. "Critical" in this case means that if the thread
  crashes for whatever reason, then the program cannot continue correctly, so
  we should crash the program instead of running in some kind of zombie broken
  state.
-}
forkC
  :: String
    -- ^ The name of the critical thread, used for logging.
  -> LIO ()
    -- ^ The IO to execute.
  -> LIO ()
forkC name io = do
  logging <- askLoggerIO
  lift . void . forkIO $ do
    result <- try (runLoggingT io logging)
    case result of
      Left err -> do
        let msg =
              "Exception caught in critical thread " ++ show name
              ++ ". We are crashing the entire program because we can't "
              ++ "continue without this thread. The error was: "
              ++ show (err :: SomeException)
        -- write the message to every place we can think of.
        (`runLoggingT` logging) . $(logError) . pack $ msg
        putStrLn msg
        hPutStrLn stderr msg
        exitImmediately (ExitFailure 1)
      Right v -> return v