summaryrefslogtreecommitdiff
path: root/src/GF/System/UseSignal.hs
blob: 5e6d812372b3233ae310e4f859a277fb71977c16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
----------------------------------------------------------------------
-- |
-- Module      : GF.System.UseSignal
-- Maintainer  : Bjorn Bringert
-- Stability   : (stability)
-- Portability : (portability)
--
-- > CVS $Date: 2005/11/11 11:12:50 $ 
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.1 $
--
-- Allows SIGINT (Ctrl-C) to interrupt computations.
-----------------------------------------------------------------------------

module GF.System.UseSignal where

import Control.Concurrent (myThreadId, killThread)
import Control.Exception (Exception,catch)
import Prelude hiding (catch)
import System.IO
import System.Posix.Signals

{-# NOINLINE runInterruptibly #-}

-- | Run an IO action, and allow it to be interrupted
--   by a SIGINT to the current process. Returns
--   an exception if the process did not complete 
--   normally.
--   NOTES: 
--   * This will replace any existing SIGINT
--     handler during the action. After the computation 
--     has completed the existing handler will be restored.
--   * If the IO action is lazy (e.g. using readFile,
--     unsafeInterleaveIO etc.) the lazy computation will
--     not be interruptible, as it will be performed
--     after the signal handler has been removed.
runInterruptibly :: IO a -> IO (Either Exception a)
runInterruptibly a = 
    do t <- myThreadId
       oldH <- installHandler sigINT (Catch (killThread t)) Nothing
       x <- p `catch` h
       installHandler sigINT oldH Nothing
       return x
  where p = a >>= \x -> return $! Right $! x
        h e = return $ Left e

-- | Like 'runInterruptibly', but always returns (), whether
--   the computation fails or not.
runInterruptibly_ :: IO () -> IO ()
runInterruptibly_ = fmap (either (const ()) id) . runInterruptibly

-- | Run an action with SIGINT blocked.
blockInterrupt :: IO a -> IO a
blockInterrupt a = 
    do oldH <- installHandler sigINT Ignore Nothing
       x <- a
       installHandler sigINT oldH Nothing
       return x