module System.Posix.Graceful.Worker
( GracefulWorker(..)
, workerProcess
) where
import Control.Concurrent ( forkIO, threadDelay )
import Control.Concurrent.STM ( atomically, newTVarIO, modifyTVar', readTVar )
import Control.Exception ( IOException, bracket, bracket_, finally, try )
import Control.Monad ( void, forever, when )
import Network ( Socket )
import Network.Socket.Wrapper ( close, accept )
import System.Exit ( ExitCode(..) )
import System.Posix.Process ( exitImmediately )
import System.Posix.Signals ( Handler(..), installHandler, sigQUIT )
data GracefulWorker = forall resource .
GracefulWorker { gracefulWorkerInitialize :: IO resource
, gracefulWorkerApplication :: Socket -> resource -> IO ()
, gracefulWorkerFinalize :: resource -> IO ()
}
tryIO :: IO a -> IO (Either IOException a)
tryIO = try
workerProcess :: GracefulWorker -> Socket -> IO ()
workerProcess GracefulWorker { gracefulWorkerInitialize = initialize
, gracefulWorkerApplication = application
, gracefulWorkerFinalize = finalize
} sock = do
void $ installHandler sigQUIT (CatchOnce $ close sock) Nothing
count <- newTVarIO (0 :: Int)
void $ tryIO $ bracket initialize finalize $ \resource ->
void $ forever $ do
(s, _) <- accept sock
let app = application s resource
forkIO $ bracket_
(atomically $ modifyTVar' count succ)
(atomically $ modifyTVar' count pred)
(app `finally` close s)
waitAllAction count
close sock
exitImmediately ExitSuccess
where
waitAllAction count = do
active <- atomically $ readTVar count
when (0 /= active) $ do
threadDelay 1000
waitAllAction count