Skip to content

Instantly share code, notes, and snippets.

@neilmayhew
Last active April 6, 2023 20:43
Show Gist options
  • Save neilmayhew/56d7a4160d29054b95ce994b74bdca1e to your computer and use it in GitHub Desktop.
Save neilmayhew/56d7a4160d29054b95ce994b74bdca1e to your computer and use it in GitHub Desktop.
Graceful handling of SIGTERM for Haskell programs
module System.GracefulTermination where
import Control.Concurrent.Async (race)
import Control.Concurrent.MVar
import Data.Functor (void)
import System.Posix.Signals
-- | Ensure that @SIGTERM@ is handled gracefully, because it's how containers are stopped.
--
-- @action@ will receive an 'AsyncCancelled' exception if @SIGTERM@ is received by the process.
--
-- Typical use:
--
-- > main :: IO ()
-- > main = withGracefulTermination_ $ do
--
-- Note that although the Haskell runtime handles @SIGINT@ it doesn't do anything with @SIGTERM@.
-- Therefore, the program will be killed immediately and no cleanup will be performed. In particular,
-- exception handlers in `bracket`, `finally`, `onException`, etc. won't be run. However, if the
-- program is running as PID 1 in a container, @SIGTERM@ will be ignored and the program will keep
-- running. This will likely result in a @SIGKILL@ being sent a short while later and cleanup still
-- won't be performed.
withGracefulTermination :: IO a -> IO (Maybe a)
withGracefulTermination action = do
var <- newEmptyMVar
let terminate = void $ tryPutMVar var ()
waitForTermination = takeMVar var
void $ installHandler sigTERM (CatchOnce terminate) Nothing
either (const Nothing) Just <$> race waitForTermination action
-- | Like 'withGracefulTermination' but ignoring the return value
withGracefulTermination_ :: IO a -> IO ()
withGracefulTermination_ = void . withGracefulTermination
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment