Skip to content

Commit 4d2c324

Browse files
committed
Handle SIGTERM by throwing an asynchronous exception
That's roughly how Ctrl-C (SIGINT) is handled by the GHC runtime. This way, killing cabal via SIGTERM will give it a chance to terminate any running children. Previously, it would exit directly, leaving children behind to exit on their own.
1 parent fa404cd commit 4d2c324

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

cabal-install/cabal-install.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ executable cabal
258258
build-depends:
259259
cabal-install,
260260
directory,
261-
filepath
261+
filepath,
262+
signal
262263

263264
-- Small, fast running tests.
264265
--

cabal-install/main/Main.hs

+27-2
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,16 @@ import System.IO ( BufferMode(LineBuffering), hSetBuffering
163163
import System.Directory ( doesFileExist, getCurrentDirectory
164164
, withCurrentDirectory)
165165
import Data.Monoid (Any(..))
166-
import Control.Exception (try)
167-
166+
import Control.Exception (try, throwTo, asyncExceptionFromException, asyncExceptionToException)
167+
import qualified System.Signal as Signal
168+
import Control.Concurrent (myThreadId)
168169

169170
-- | Entry point
170171
--
171172
main :: IO ()
172173
main = do
174+
installTerminationHandler
175+
173176
-- Enable line buffering so that we can get fast feedback even when piped.
174177
-- This is especially important for CI and build systems.
175178
hSetBuffering stdout LineBuffering
@@ -181,6 +184,28 @@ main = do
181184
(args0, args1) <- break (== "--") <$> getArgs
182185
mainWorker =<< (++ args1) <$> expandResponse args0
183186

187+
-- | Terminated is an asynchronous exception, thrown when
188+
-- SIGTERM is received. It's to 'kill' what 'UserInterrupt'
189+
-- is to Ctrl-C.
190+
data Terminated = Terminated
191+
192+
instance Exception Terminated where
193+
toException = asyncExceptionToException
194+
fromException = asyncExceptionFromException
195+
196+
instance Show Terminated where
197+
show Terminated = "terminated"
198+
199+
-- | Install a signal handler that initiates a controlled shutdown on receiving
200+
-- SIGTERM by throwing an asynchronous exception at the main thread. Must be
201+
-- called from the main thread.
202+
installTerminationHandler :: IO ()
203+
installTerminationHandler = do
204+
mainThreadId <- myThreadId
205+
Signal.installHandler
206+
Signal.sigTERM
207+
(\_ -> throwTo mainThreadId Terminated)
208+
184209
mainWorker :: [String] -> IO ()
185210
mainWorker args = do
186211
maybeScriptAndArgs <- case args of

0 commit comments

Comments
 (0)