summaryrefslogtreecommitdiff
path: root/testsuite/timeout/timeout.hs
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/timeout/timeout.hs')
-rw-r--r--testsuite/timeout/timeout.hs173
1 files changed, 42 insertions, 131 deletions
diff --git a/testsuite/timeout/timeout.hs b/testsuite/timeout/timeout.hs
index 9f3044f36d..a156fbbce3 100644
--- a/testsuite/timeout/timeout.hs
+++ b/testsuite/timeout/timeout.hs
@@ -3,27 +3,15 @@
module Main where
import Control.Concurrent (forkIO, threadDelay)
-import Control.Concurrent.MVar (putMVar, takeMVar, newEmptyMVar)
+import Control.Concurrent.MVar
import Control.Monad
import Control.Exception
import Data.Maybe (isNothing)
import System.Environment (getArgs)
import System.Exit
+import System.Process
import System.IO (hPutStrLn, stderr)
-
-#if !defined(mingw32_HOST_OS)
-import System.Posix hiding (killProcess)
import System.IO.Error hiding (try,catch)
-#endif
-
-#if defined(mingw32_HOST_OS)
-import System.Process
-import WinCBindings
-import Foreign
-import System.Win32.DebugApi
-import System.Win32.Types
-import System.Win32.Console.CtrlHandler
-#endif
main :: IO ()
main = do
@@ -35,22 +23,40 @@ main = do
_ -> die ("Can't parse " ++ show secs ++ " as a number of seconds")
_ -> die ("Bad arguments " ++ show args)
+data FinishedReason
+ = TimedOut
+ | Exited ExitCode
+ | InterruptedSignal
+ | OtherError SomeException
+
run :: Int -> String -> IO ()
-#if !defined(mingw32_HOST_OS)
run secs cmd = do
- m <- newEmptyMVar
- mp <- newEmptyMVar
- installHandler sigINT (Catch (putMVar m Nothing)) Nothing
- forkIO $ do threadDelay (secs * 1000000)
- putMVar m Nothing
- forkIO $ do ei <- try $ do pid <- systemSession cmd
- return pid
- putMVar mp ei
- case ei of
- Left _ -> return ()
- Right pid -> do
- r <- getProcessStatus True False pid
- putMVar m r
+ m <- newEmptyMVar :: IO (MVar FinishedReason)
+ mp <- newEmptyMVar :: IO (MVar (Either IOException ProcessHandle))
+
+ -- The timeout thread
+ forkIO $ do
+ threadDelay (secs * 1000000)
+ putMVar m TimedOut
+
+ -- the process itself
+ forkIO $ handle (\exc -> putMVar mp $ Left (userError $ show (exc :: SomeException))) $ do
+ ei <- fmap (fmap (\(_,_,_,ph) -> ph)) $ try $ createProcess (shell cmd)
+ { new_session = True
+ , use_process_jobs = True
+ }
+ putMVar mp ei
+ case ei of
+ Left _ -> return ()
+ Right pid -> do
+ r <- waitForProcess pid
+ putMVar m (Exited r)
+
+ -- Be sure to catch SIGINT while waiting
+ let handleINT UserInterrupt = putMVar m InterruptedSignal
+ handleINT other = throwIO other
+
+ handle handleINT $ do
ei_pid_ph <- takeMVar mp
case ei_pid_ph of
Left e -> do hPutStrLn stderr
@@ -59,107 +65,12 @@ run secs cmd = do
Right pid -> do
r <- takeMVar m
case r of
- Nothing -> do
- killProcess pid
- exitWith (ExitFailure 99)
- Just (Exited r) -> exitWith r
- Just (Terminated s) -> raiseSignal s
- Just _ -> exitWith (ExitFailure 1)
-
-systemSession cmd =
- forkProcess $ do
- createSession
- executeFile "/bin/sh" False ["-c", cmd] Nothing
- -- need to use exec() directly here, rather than something like
- -- System.Process.system, because we are in a forked child and some
- -- pthread libraries get all upset if you start doing certain
- -- things in a forked child of a pthread process, such as forking
- -- more threads.
-
-killProcess pid = do
- ignoreIOExceptions (signalProcessGroup sigTERM pid)
- checkReallyDead 10
- where
- checkReallyDead 0 = hPutStrLn stderr "checkReallyDead: Giving up"
- checkReallyDead (n+1) =
- do threadDelay (3*100000) -- 3/10 sec
- m <- tryJust (guard . isDoesNotExistError) $
- getProcessStatus False False pid
- case m of
- Right Nothing -> return ()
- Left _ -> return ()
- _ -> do
- ignoreIOExceptions (signalProcessGroup sigKILL pid)
- checkReallyDead n
-
-ignoreIOExceptions :: IO () -> IO ()
-ignoreIOExceptions io = io `catch` ((\_ -> return ()) :: IOException -> IO ())
-
-#else
-run secs cmd =
- let escape '\\' = "\\\\"
- escape '"' = "\\\""
- escape c = [c]
- cmd' = "sh -c \"" ++ concatMap escape cmd ++ "\"" in
- alloca $ \p_startupinfo ->
- alloca $ \p_pi ->
- withTString cmd' $ \cmd'' ->
- do job <- createJobObjectW nullPtr nullPtr
- b_info <- setJobParameters job
- unless b_info $ errorWin "setJobParameters"
-
- ioPort <- createCompletionPort job
- when (ioPort == nullPtr) $ errorWin "createCompletionPort, cannot continue."
-
- -- We're explicitly turning off handle inheritance to prevent misc handles
- -- from being inherited by the child. Notable we don't want the I/O Completion
- -- Ports and Job handles to be inherited. So we mark them as non-inheritable.
- setHandleInformation job cHANDLE_FLAG_INHERIT 0
- setHandleInformation ioPort cHANDLE_FLAG_INHERIT 0
-
- -- Now create the process suspended so we can add it to the job and then resume.
- -- This is so we don't miss any events on the receiving end of the I/O port.
- let creationflags = cCREATE_SUSPENDED
- b <- createProcessW nullPtr cmd'' nullPtr nullPtr True
- creationflags
- nullPtr nullPtr p_startupinfo p_pi
- unless b $ errorWin "createProcessW"
-
- pi <- peek p_pi
- b_assign <- assignProcessToJobObject job (piProcess pi)
- unless b_assign $ errorWin "assignProcessToJobObject, cannot continue."
-
- let handleInterrupt action =
- action `onException` terminateJobObject job 99
- handleCtrl _ = do
- terminateJobObject job 99
- closeHandle ioPort
- closeHandle job
- exitWith (ExitFailure 99)
- return True
-
- withConsoleCtrlHandler handleCtrl $
- handleInterrupt $ do
- resumeThread (piThread pi)
- -- The program is now running
- let handle = piProcess pi
- let millisecs = secs * 1000
- rc <- waitForJobCompletion job ioPort (fromIntegral millisecs)
- closeHandle ioPort
-
- if not rc
- then do terminateJobObject job 99
- closeHandle job
+ TimedOut -> do
+ interruptProcessGroupOf pid
+ terminateProcess pid
exitWith (ExitFailure 99)
- else alloca $ \p_exitCode ->
- do terminateJobObject job 0
- -- Ensured it's all really dead.
- closeHandle job
- r <- getExitCodeProcess handle p_exitCode
- if r
- then peek p_exitCode >>= \case
- 0 -> exitWith ExitSuccess
- e -> exitWith $ ExitFailure (fromIntegral e)
- else errorWin "getExitCodeProcess"
-#endif
-
+ InterruptedSignal -> do
+ interruptProcessGroupOf pid
+ terminateProcess pid
+ exitWith (ExitFailure 2)
+ Exited r -> exitWith r