X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=GHC%2FConc.lhs;h=431882503b10a7f21ee7757614e15f1e5301d8e7;hb=8afc9fecd586d3c4f7ef9c69fb1686a79e5f441d;hp=1b6103652fc5b9d4c49c79b6ba6b94a567cb847b;hpb=34a1ed6a7c6639b49ba2fee733c2c7387af6a2e0;p=ghc-base.git diff --git a/GHC/Conc.lhs b/GHC/Conc.lhs index 1b61036..4318825 100644 --- a/GHC/Conc.lhs +++ b/GHC/Conc.lhs @@ -1,5 +1,6 @@ \begin{code} {-# OPTIONS_GHC -XNoImplicitPrelude #-} +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} {-# OPTIONS_HADDOCK not-home #-} ----------------------------------------------------------------------------- -- | @@ -36,6 +37,7 @@ module GHC.Conc , throwTo -- :: ThreadId -> Exception -> IO () , par -- :: a -> b -> b , pseq -- :: a -> b -> b + , runSparks , yield -- :: IO () , labelThread -- :: ThreadId -> String -> IO () @@ -48,17 +50,6 @@ module GHC.Conc , threadWaitRead -- :: Int -> IO () , threadWaitWrite -- :: Int -> IO () - -- * MVars - , MVar(..) - , newMVar -- :: a -> IO (MVar a) - , newEmptyMVar -- :: IO (MVar a) - , takeMVar -- :: MVar a -> IO a - , putMVar -- :: MVar a -> a -> IO () - , tryTakeMVar -- :: MVar a -> IO (Maybe a) - , tryPutMVar -- :: MVar a -> a -> IO Bool - , isEmptyMVar -- :: MVar a -> IO Bool - , addMVarFinalizer -- :: MVar a -> IO () -> IO () - -- * TVars , STM(..) , atomically -- :: STM a -> IO a @@ -71,10 +62,12 @@ module GHC.Conc , newTVar -- :: a -> STM (TVar a) , newTVarIO -- :: a -> STM (TVar a) , readTVar -- :: TVar a -> STM a + , readTVarIO -- :: TVar a -> IO a , writeTVar -- :: a -> TVar a -> STM () , unsafeIOToSTM -- :: IO a -> STM a -- * Miscellaneous + , withMVar #ifdef mingw32_HOST_OS , asyncRead -- :: Int -> Int -> Int -> Ptr a -> IO (Int, Int) , asyncWrite -- :: Int -> Int -> Int -> Ptr a -> IO (Int, Int) @@ -85,16 +78,23 @@ module GHC.Conc #endif #ifndef mingw32_HOST_OS - , signalHandlerLock + , Signal, HandlerFun, setHandler, runHandlers #endif , ensureIOManagerIsRunning +#ifndef mingw32_HOST_OS + , syncIOManager +#endif #ifdef mingw32_HOST_OS , ConsoleEvent(..) , win32ConsoleHandler , toWin32ConsoleEvent #endif + , setUncaughtExceptionHandler -- :: (Exception -> IO ()) -> IO () + , getUncaughtExceptionHandler -- :: IO (Exception -> IO ()) + + , reportError, reportStackOverflow ) where import System.Posix.Types @@ -104,29 +104,39 @@ import System.Posix.Internals import Foreign import Foreign.C -#ifndef __HADDOCK__ -import {-# SOURCE #-} GHC.TopHandler ( reportError, reportStackOverflow ) +#ifndef mingw32_HOST_OS +import Data.Dynamic +import Control.Monad #endif - import Data.Maybe import GHC.Base -import GHC.IOBase +import {-# SOURCE #-} GHC.IO.Handle ( hFlush ) +import {-# SOURCE #-} GHC.IO.Handle.FD ( stdout ) +import GHC.IO +import GHC.IO.Exception +import GHC.Exception +import GHC.IORef +import GHC.MVar import GHC.Num ( Num(..) ) -import GHC.Real ( fromIntegral, div ) +import GHC.Real ( fromIntegral ) #ifndef mingw32_HOST_OS -import GHC.Base ( Int(..) ) +import GHC.IOArray +import GHC.Arr ( inRange ) +#endif +#ifdef mingw32_HOST_OS +import GHC.Real ( div ) +import GHC.Ptr ( FunPtr(..) ) #endif #ifdef mingw32_HOST_OS import GHC.Read ( Read ) import GHC.Enum ( Enum ) #endif -import GHC.Exception ( SomeException(..), throw ) import GHC.Pack ( packCString# ) -import GHC.Ptr ( Ptr(..), plusPtr, FunPtr(..) ) -import GHC.STRef +import GHC.Ptr ( Ptr(..) ) import GHC.Show ( Show(..), showString ) import Data.Typeable +import GHC.Err infixr 0 `par`, `pseq` \end{code} @@ -199,10 +209,15 @@ library that uses thread-local storage, use 'Control.Concurrent.forkOS' instead. GHC note: the new thread inherits the /blocked/ state of the parent (see 'Control.Exception.block'). + +The newly created thread has an exception handler that discards the +exceptions 'BlockedOnDeadMVar', 'BlockedIndefinitely', and +'ThreadKilled', and passes all other exceptions to the uncaught +exception handler (see 'setUncaughtExceptionHandler'). -} forkIO :: IO () -> IO ThreadId forkIO action = IO $ \ s -> - case (fork# action_plus s) of (# s1, id #) -> (# s1, ThreadId id #) + case (fork# action_plus s) of (# s1, tid #) -> (# s1, ThreadId tid #) where action_plus = catchException action childHandler @@ -221,7 +236,7 @@ equivalent). -} forkOnIO :: Int -> IO () -> IO ThreadId forkOnIO (I# cpu) action = IO $ \ s -> - case (forkOn# cpu action_plus s) of (# s1, id #) -> (# s1, ThreadId id #) + case (forkOn# cpu action_plus s) of (# s1, tid #) -> (# s1, ThreadId tid #) where action_plus = catchException action childHandler @@ -234,8 +249,11 @@ numCapabilities = unsafePerformIO $ do n <- peek n_capabilities return (fromIntegral n) +#if defined(mingw32_HOST_OS) && defined(__PIC__) +foreign import ccall "_imp__n_capabilities" n_capabilities :: Ptr CInt +#else foreign import ccall "&n_capabilities" n_capabilities :: Ptr CInt - +#endif childHandler :: SomeException -> IO () childHandler err = catchException (real_handler err) childHandler @@ -260,11 +278,12 @@ The memory used by the thread will be garbage collected if it isn\'t referenced from anywhere. The 'killThread' function is defined in terms of 'throwTo': -> killThread tid = throwTo tid (AsyncException ThreadKilled) +> killThread tid = throwTo tid ThreadKilled +Killthread is a no-op if the target thread has already completed. -} killThread :: ThreadId -> IO () -killThread tid = throwTo tid (toException ThreadKilled) +killThread tid = throwTo tid ThreadKilled {- | 'throwTo' raises an arbitrary exception in the target thread (GHC only). @@ -286,25 +305,24 @@ the paper \"Asynchronous exceptions in Haskell\" (). In the paper, 'throwTo' is non-blocking; but the library implementation adopts a more synchronous design in which 'throwTo' does not return until the exception -is received by the target thread. The trade-off is discussed in Section 8 of the paper. -Like any blocking operation, 'throwTo' is therefore interruptible (see Section 4.3 of +is received by the target thread. The trade-off is discussed in Section 9 of the paper. +Like any blocking operation, 'throwTo' is therefore interruptible (see Section 5.3 of the paper). There is currently no guarantee that the exception delivered by 'throwTo' will be -delivered at the first possible opportunity. In particular, if a thread may +delivered at the first possible opportunity. In particular, a thread may unblock and then re-block exceptions (using 'unblock' and 'block') without receiving a pending 'throwTo'. This is arguably undesirable behaviour. -} --- XXX This is duplicated in Control.{Old,}Exception -throwTo :: ThreadId -> SomeException -> IO () -throwTo (ThreadId id) ex = IO $ \ s -> - case (killThread# id ex s) of s1 -> (# s1, () #) +throwTo :: Exception e => ThreadId -> e -> IO () +throwTo (ThreadId tid) ex = IO $ \ s -> + case (killThread# tid (toException ex) s) of s1 -> (# s1, () #) -- | Returns the 'ThreadId' of the calling thread (GHC only). myThreadId :: IO ThreadId myThreadId = IO $ \s -> - case (myThreadId# s) of (# s1, id #) -> (# s1, ThreadId id #) + case (myThreadId# s) of (# s1, tid #) -> (# s1, ThreadId tid #) -- |The 'yield' action allows (forces, in a co-operative multitasking @@ -327,8 +345,8 @@ Other applications like the graphical Concurrent Haskell Debugger labelThread :: ThreadId -> String -> IO () labelThread (ThreadId t) str = IO $ \ s -> - let ps = packCString# str - adr = byteArrayContents# ps in + let !ps = packCString# str + !adr = byteArrayContents# ps in case (labelThread# t adr s) of s1 -> (# s1, () #) -- Nota Bene: 'pseq' used to be 'seq' @@ -350,6 +368,13 @@ pseq x y = x `seq` lazy y par :: a -> b -> b par x y = case (par# x) of { _ -> lazy y } +-- | Internal function used by the RTS to run sparks. +runSparks :: IO () +runSparks = IO loop + where loop s = case getSpark# s of + (# s', n, p #) -> + if n ==# 0# then (# s', () #) + else p `seq` loop s' data BlockReason = BlockedOnMVar @@ -437,7 +462,7 @@ bindSTM (STM m) k = STM ( \s -> thenSTM :: STM a -> STM b -> STM b thenSTM (STM m) k = STM ( \s -> case m s of - (# new_s, a #) -> unSTM k new_s + (# new_s, _ #) -> unSTM k new_s ) returnSTM :: a -> STM a @@ -546,6 +571,16 @@ newTVarIO val = IO $ \s1# -> case newTVar# val s1# of (# s2#, tvar# #) -> (# s2#, TVar tvar# #) +-- |Return the current value stored in a TVar. +-- This is equivalent to +-- +-- > readTVarIO = atomically . readTVar +-- +-- but works much faster, because it doesn't perform a complete +-- transaction, it just reads the current value of the 'TVar'. +readTVarIO :: TVar a -> IO a +readTVarIO (TVar tvar#) = IO $ \s# -> readTVarIO# tvar# s# + -- |Return the current value stored in a TVar readTVar :: TVar a -> STM a readTVar (TVar tvar#) = STM $ \s# -> readTVar# tvar# s# @@ -558,109 +593,9 @@ writeTVar (TVar tvar#) val = STM $ \s1# -> \end{code} -%************************************************************************ -%* * -\subsection[mvars]{M-Structures} -%* * -%************************************************************************ - -M-Vars are rendezvous points for concurrent threads. They begin -empty, and any attempt to read an empty M-Var blocks. When an M-Var -is written, a single blocked thread may be freed. Reading an M-Var -toggles its state from full back to empty. Therefore, any value -written to an M-Var may only be read once. Multiple reads and writes -are allowed, but there must be at least one read between any two -writes. +MVar utilities \begin{code} ---Defined in IOBase to avoid cycle: data MVar a = MVar (SynchVar# RealWorld a) - --- |Create an 'MVar' which is initially empty. -newEmptyMVar :: IO (MVar a) -newEmptyMVar = IO $ \ s# -> - case newMVar# s# of - (# s2#, svar# #) -> (# s2#, MVar svar# #) - --- |Create an 'MVar' which contains the supplied value. -newMVar :: a -> IO (MVar a) -newMVar value = - newEmptyMVar >>= \ mvar -> - putMVar mvar value >> - return mvar - --- |Return the contents of the 'MVar'. If the 'MVar' is currently --- empty, 'takeMVar' will wait until it is full. After a 'takeMVar', --- the 'MVar' is left empty. --- --- There are two further important properties of 'takeMVar': --- --- * 'takeMVar' is single-wakeup. That is, if there are multiple --- threads blocked in 'takeMVar', and the 'MVar' becomes full, --- only one thread will be woken up. The runtime guarantees that --- the woken thread completes its 'takeMVar' operation. --- --- * When multiple threads are blocked on an 'MVar', they are --- woken up in FIFO order. This is useful for providing --- fairness properties of abstractions built using 'MVar's. --- -takeMVar :: MVar a -> IO a -takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s# - --- |Put a value into an 'MVar'. If the 'MVar' is currently full, --- 'putMVar' will wait until it becomes empty. --- --- There are two further important properties of 'putMVar': --- --- * 'putMVar' is single-wakeup. That is, if there are multiple --- threads blocked in 'putMVar', and the 'MVar' becomes empty, --- only one thread will be woken up. The runtime guarantees that --- the woken thread completes its 'putMVar' operation. --- --- * When multiple threads are blocked on an 'MVar', they are --- woken up in FIFO order. This is useful for providing --- fairness properties of abstractions built using 'MVar's. --- -putMVar :: MVar a -> a -> IO () -putMVar (MVar mvar#) x = IO $ \ s# -> - case putMVar# mvar# x s# of - s2# -> (# s2#, () #) - --- |A non-blocking version of 'takeMVar'. The 'tryTakeMVar' function --- returns immediately, with 'Nothing' if the 'MVar' was empty, or --- @'Just' a@ if the 'MVar' was full with contents @a@. After 'tryTakeMVar', --- the 'MVar' is left empty. -tryTakeMVar :: MVar a -> IO (Maybe a) -tryTakeMVar (MVar m) = IO $ \ s -> - case tryTakeMVar# m s of - (# s, 0#, _ #) -> (# s, Nothing #) -- MVar is empty - (# s, _, a #) -> (# s, Just a #) -- MVar is full - --- |A non-blocking version of 'putMVar'. The 'tryPutMVar' function --- attempts to put the value @a@ into the 'MVar', returning 'True' if --- it was successful, or 'False' otherwise. -tryPutMVar :: MVar a -> a -> IO Bool -tryPutMVar (MVar mvar#) x = IO $ \ s# -> - case tryPutMVar# mvar# x s# of - (# s, 0# #) -> (# s, False #) - (# s, _ #) -> (# s, True #) - --- |Check whether a given 'MVar' is empty. --- --- Notice that the boolean value returned is just a snapshot of --- the state of the MVar. By the time you get to react on its result, --- the MVar may have been filled (or emptied) - so be extremely --- careful when using this operation. Use 'tryTakeMVar' instead if possible. -isEmptyMVar :: MVar a -> IO Bool -isEmptyMVar (MVar mv#) = IO $ \ s# -> - case isEmptyMVar# mv# s# of - (# s2#, flg #) -> (# s2#, not (flg ==# 0#) #) - --- |Add a finalizer to an 'MVar' (GHC only). See "Foreign.ForeignPtr" and --- "System.Mem.Weak" for more about finalizers. -addMVarFinalizer :: MVar a -> IO () -> IO () -addMVarFinalizer (MVar m) finalizer = - IO $ \s -> case mkWeak# m () finalizer s of { (# s1, w #) -> (# s1, () #) } - withMVar :: MVar a -> (a -> IO b) -> IO b withMVar m io = block $ do @@ -671,7 +606,6 @@ withMVar m io = return b \end{code} - %************************************************************************ %* * \subsection{Thread waiting} @@ -688,19 +622,19 @@ withMVar m io = asyncRead :: Int -> Int -> Int -> Ptr a -> IO (Int, Int) asyncRead (I# fd) (I# isSock) (I# len) (Ptr buf) = IO $ \s -> case asyncRead# fd isSock len buf s of - (# s, len#, err# #) -> (# s, (I# len#, I# err#) #) + (# s', len#, err# #) -> (# s', (I# len#, I# err#) #) asyncWrite :: Int -> Int -> Int -> Ptr a -> IO (Int, Int) asyncWrite (I# fd) (I# isSock) (I# len) (Ptr buf) = IO $ \s -> case asyncWrite# fd isSock len buf s of - (# s, len#, err# #) -> (# s, (I# len#, I# err#) #) + (# s', len#, err# #) -> (# s', (I# len#, I# err#) #) asyncDoProc :: FunPtr (Ptr a -> IO Int) -> Ptr a -> IO Int asyncDoProc (FunPtr proc) (Ptr param) = -- the 'length' value is ignored; simplifies implementation of -- the async*# primops to have them all return the same result. IO $ \s -> case asyncDoProc# proc param s of - (# s, len#, err# #) -> (# s, I# err# #) + (# s', _len#, err# #) -> (# s', I# err# #) -- to aid the use of these primops by the IO Handle implementation, -- provide the following convenience funs: @@ -728,7 +662,7 @@ threadWaitRead fd #endif | otherwise = IO $ \s -> case fromIntegral fd of { I# fd# -> - case waitRead# fd# s of { s -> (# s, () #) + case waitRead# fd# s of { s' -> (# s', () #) }} -- | Block the current thread until data can be written to the @@ -740,7 +674,7 @@ threadWaitWrite fd #endif | otherwise = IO $ \s -> case fromIntegral fd of { I# fd# -> - case waitWrite# fd# s of { s -> (# s, () #) + case waitWrite# fd# s of { s' -> (# s', () #) }} -- | Suspends the current thread for a given number of microseconds @@ -755,7 +689,7 @@ threadDelay time | threaded = waitForDelayEvent time | otherwise = IO $ \s -> case fromIntegral time of { I# time# -> - case delay# time# s of { s -> (# s, () #) + case delay# time# s of { s' -> (# s', () #) }} @@ -866,10 +800,6 @@ delayTime (DelaySTM t _) = t type USecs = Word64 --- XXX: move into GHC.IOBase from Data.IORef? -atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b -atomicModifyIORef (IORef (STRef r#)) f = IO $ \s -> atomicModifyMutVar# r# f s - foreign import ccall unsafe "getUSecOfDay" getUSecOfDay :: IO USecs @@ -908,26 +838,29 @@ service_loop wakeup old_delays = do case r of 0xffffffff -> do c_maperrno; throwErrno "service_loop" 0 -> do - r <- c_readIOManagerEvent + r2 <- c_readIOManagerEvent exit <- - case r of - _ | r == io_MANAGER_WAKEUP -> return False - _ | r == io_MANAGER_DIE -> return True + case r2 of + _ | r2 == io_MANAGER_WAKEUP -> return False + _ | r2 == io_MANAGER_DIE -> return True 0 -> return False -- spurious wakeup - r -> do start_console_handler (r `shiftR` 1); return False + _ -> do start_console_handler (r2 `shiftR` 1); return False if exit then return () else service_cont wakeup delays' _other -> service_cont wakeup delays' -- probably timeout +service_cont :: HANDLE -> [DelayReq] -> IO () service_cont wakeup delays = do - atomicModifyIORef prodding (\_ -> (False,False)) + r <- atomicModifyIORef prodding (\_ -> (False,False)) + r `seq` return () -- avoid space leak service_loop wakeup delays -- must agree with rts/win32/ThrIOManager.c -io_MANAGER_WAKEUP = 0xffffffff :: Word32 -io_MANAGER_DIE = 0xfffffffe :: Word32 +io_MANAGER_WAKEUP, io_MANAGER_DIE :: Word32 +io_MANAGER_WAKEUP = 0xffffffff +io_MANAGER_DIE = 0xfffffffe data ConsoleEvent = ControlC @@ -946,6 +879,7 @@ start_console_handler r = return () Nothing -> return () +toWin32ConsoleEvent :: Num a => a -> Maybe ConsoleEvent toWin32ConsoleEvent ev = case ev of 0 {- CTRL_C_EVENT-} -> Just ControlC @@ -958,19 +892,21 @@ toWin32ConsoleEvent ev = win32ConsoleHandler :: MVar (ConsoleEvent -> IO ()) win32ConsoleHandler = unsafePerformIO (newMVar (error "win32ConsoleHandler")) +-- XXX Is this actually needed? stick :: IORef HANDLE {-# NOINLINE stick #-} stick = unsafePerformIO (newIORef nullPtr) +wakeupIOManager :: IO () wakeupIOManager = do - hdl <- readIORef stick + _hdl <- readIORef stick c_sendIOManagerEvent io_MANAGER_WAKEUP -- Walk the queue of pending delays, waking up any that have passed -- and return the smallest delay to wait for. The queue of pending -- delays is kept ordered. getDelay :: USecs -> [DelayReq] -> IO ([DelayReq], DWORD) -getDelay now [] = return ([], iNFINITE) +getDelay _ [] = return ([], iNFINITE) getDelay now all@(d : rest) = case d of Delay time m | now >= time -> do @@ -991,7 +927,8 @@ getDelay now all@(d : rest) type HANDLE = Ptr () type DWORD = Word32 -iNFINITE = 0xFFFFFFFF :: DWORD -- urgh +iNFINITE :: DWORD +iNFINITE = 0xFFFFFFFF -- urgh foreign import ccall unsafe "getIOManagerEvent" -- in the RTS (ThrIOManager.c) c_getIOManagerEvent :: IO HANDLE @@ -1018,6 +955,10 @@ startIOManagerThread = do throwErrnoIfMinus1 "startIOManagerThread" (c_pipe fds) rd_end <- peekElemOff fds 0 wr_end <- peekElemOff fds 1 + setNonBlockingFD wr_end True -- writes happen in a signal handler, we + -- don't want them to block. + setCloseOnExec rd_end + setCloseOnExec wr_end writeIORef stick (fromIntegral wr_end) c_setIOManagerPipe wr_end forkIO $ do @@ -1043,7 +984,7 @@ service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do -- pick up new delay requests new_delays <- atomicModifyIORef pendingDelays (\a -> ([],a)) - let delays = foldr insertDelay old_delays new_delays + let delays0 = foldr insertDelay old_delays new_delays -- build the FDSets for select() fdZero readfds @@ -1076,7 +1017,7 @@ service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do else return (False,delays') - (wakeup_all,delays') <- do_select delays + (wakeup_all,delays') <- do_select delays0 exit <- if wakeup_all then return False @@ -1085,17 +1026,24 @@ service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do if b == 0 then return False else alloca $ \p -> do - c_read (fromIntegral wakeup) p 1; return () + c_read (fromIntegral wakeup) p 1 s <- peek p case s of _ | s == io_MANAGER_WAKEUP -> return False _ | s == io_MANAGER_DIE -> return True - _ -> withMVar signalHandlerLock $ \_ -> do - handler_tbl <- peek handlers - sp <- peekElemOff handler_tbl (fromIntegral s) - io <- deRefStablePtr sp - forkIO io - return False + _ | s == io_MANAGER_SYNC -> do + mvars <- readIORef sync + mapM_ (flip putMVar ()) mvars + return False + _ -> do + fp <- mallocForeignPtrBytes (fromIntegral sizeof_siginfo_t) + withForeignPtr fp $ \p_siginfo -> do + r <- c_read (fromIntegral wakeup) (castPtr p_siginfo) + sizeof_siginfo_t + when (r /= fromIntegral sizeof_siginfo_t) $ + error "failed to read siginfo_t" + runHandlers' fp (fromIntegral s) + return False if exit then return () else do @@ -1106,45 +1054,116 @@ service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do service_loop wakeup readfds writefds ptimeval reqs' delays' -io_MANAGER_WAKEUP = 0xff :: CChar -io_MANAGER_DIE = 0xfe :: CChar +io_MANAGER_WAKEUP, io_MANAGER_DIE, io_MANAGER_SYNC :: Word8 +io_MANAGER_WAKEUP = 0xff +io_MANAGER_DIE = 0xfe +io_MANAGER_SYNC = 0xfd +-- | the stick is for poking the IO manager with stick :: IORef Fd {-# NOINLINE stick #-} stick = unsafePerformIO (newIORef 0) +{-# NOINLINE sync #-} +sync :: IORef [MVar ()] +sync = unsafePerformIO (newIORef []) + +-- waits for the IO manager to drain the pipe +syncIOManager :: IO () +syncIOManager = do + m <- newEmptyMVar + atomicModifyIORef sync (\old -> (m:old,())) + fd <- readIORef stick + with io_MANAGER_SYNC $ \pbuf -> do + c_write (fromIntegral fd) pbuf 1; return () + takeMVar m + wakeupIOManager :: IO () wakeupIOManager = do fd <- readIORef stick with io_MANAGER_WAKEUP $ \pbuf -> do c_write (fromIntegral fd) pbuf 1; return () --- Lock used to protect concurrent access to signal_handlers. Symptom of --- this race condition is #1922, although that bug was on Windows a similar --- bug also exists on Unix. -signalHandlerLock :: MVar () -signalHandlerLock = unsafePerformIO (newMVar ()) - -foreign import ccall "&signal_handlers" handlers :: Ptr (Ptr (StablePtr (IO ()))) +-- For the non-threaded RTS +runHandlers :: Ptr Word8 -> Int -> IO () +runHandlers p_info sig = do + fp <- mallocForeignPtrBytes (fromIntegral sizeof_siginfo_t) + withForeignPtr fp $ \p -> do + copyBytes p p_info (fromIntegral sizeof_siginfo_t) + free p_info + runHandlers' fp (fromIntegral sig) + +runHandlers' :: ForeignPtr Word8 -> Signal -> IO () +runHandlers' p_info sig = do + let int = fromIntegral sig + withMVar signal_handlers $ \arr -> + if not (inRange (boundsIOArray arr) int) + then return () + else do handler <- unsafeReadIOArray arr int + case handler of + Nothing -> return () + Just (f,_) -> do forkIO (f p_info); return () foreign import ccall "setIOManagerPipe" c_setIOManagerPipe :: CInt -> IO () +foreign import ccall "__hscore_sizeof_siginfo_t" + sizeof_siginfo_t :: CSize + +type Signal = CInt + +maxSig = 64 :: Int + +type HandlerFun = ForeignPtr Word8 -> IO () + +-- Lock used to protect concurrent access to signal_handlers. Symptom of +-- this race condition is #1922, although that bug was on Windows a similar +-- bug also exists on Unix. +{-# NOINLINE signal_handlers #-} +signal_handlers :: MVar (IOArray Int (Maybe (HandlerFun,Dynamic))) +signal_handlers = unsafePerformIO $ do + arr <- newIOArray (0,maxSig) Nothing + m <- newMVar arr + block $ do + stable_ref <- newStablePtr m + let ref = castStablePtrToPtr stable_ref + ref2 <- getOrSetSignalHandlerStore ref + if ref==ref2 + then return m + else do freeStablePtr stable_ref + deRefStablePtr (castPtrToStablePtr ref2) + +foreign import ccall unsafe "getOrSetSignalHandlerStore" + getOrSetSignalHandlerStore :: Ptr a -> IO (Ptr a) + +setHandler :: Signal -> Maybe (HandlerFun,Dynamic) -> IO (Maybe (HandlerFun,Dynamic)) +setHandler sig handler = do + let int = fromIntegral sig + withMVar signal_handlers $ \arr -> + if not (inRange (boundsIOArray arr) int) + then error "GHC.Conc.setHandler: signal out of range" + else do old <- unsafeReadIOArray arr int + unsafeWriteIOArray arr int handler + return old + -- ----------------------------------------------------------------------------- -- IO requests -buildFdSets maxfd readfds writefds [] = return maxfd -buildFdSets maxfd readfds writefds (Read fd m : reqs) +buildFdSets :: Fd -> Ptr CFdSet -> Ptr CFdSet -> [IOReq] -> IO Fd +buildFdSets maxfd _ _ [] = return maxfd +buildFdSets maxfd readfds writefds (Read fd _ : reqs) | fd >= fD_SETSIZE = error "buildFdSets: file descriptor out of range" | otherwise = do fdSet fd readfds buildFdSets (max maxfd fd) readfds writefds reqs -buildFdSets maxfd readfds writefds (Write fd m : reqs) +buildFdSets maxfd readfds writefds (Write fd _ : reqs) | fd >= fD_SETSIZE = error "buildFdSets: file descriptor out of range" | otherwise = do fdSet fd writefds buildFdSets (max maxfd fd) readfds writefds reqs +completeRequests :: [IOReq] -> Ptr CFdSet -> Ptr CFdSet -> [IOReq] + -> IO [IOReq] completeRequests [] _ _ reqs' = return reqs' completeRequests (Read fd m : reqs) readfds writefds reqs' = do b <- fdIsSet fd readfds @@ -1157,9 +1176,10 @@ completeRequests (Write fd m : reqs) readfds writefds reqs' = do then do putMVar m (); completeRequests reqs readfds writefds reqs' else completeRequests reqs readfds writefds (Write fd m : reqs') +wakeupAll :: [IOReq] -> IO () wakeupAll [] = return () -wakeupAll (Read fd m : reqs) = do putMVar m (); wakeupAll reqs -wakeupAll (Write fd m : reqs) = do putMVar m (); wakeupAll reqs +wakeupAll (Read _ m : reqs) = do putMVar m (); wakeupAll reqs +wakeupAll (Write _ m : reqs) = do putMVar m (); wakeupAll reqs waitForReadEvent :: Fd -> IO () waitForReadEvent fd = do @@ -1182,7 +1202,7 @@ waitForWriteEvent fd = do -- and return the smallest delay to wait for. The queue of pending -- delays is kept ordered. getDelay :: USecs -> Ptr CTimeVal -> [DelayReq] -> IO ([DelayReq], Ptr CTimeVal) -getDelay now ptimeval [] = return ([],nullPtr) +getDelay _ _ [] = return ([],nullPtr) getDelay now ptimeval all@(d : rest) = case d of Delay time m | now >= time -> do @@ -1195,7 +1215,7 @@ getDelay now ptimeval all@(d : rest) setTimevalTicks ptimeval (delayTime d - now) return (all,ptimeval) -newtype CTimeVal = CTimeVal () +data CTimeVal foreign import ccall unsafe "sizeofTimeVal" sizeofTimeVal :: Int @@ -1214,7 +1234,7 @@ foreign import ccall unsafe "setTimevalTicks" -- ToDo: move to System.Posix.Internals? -newtype CFdSet = CFdSet () +data CFdSet foreign import ccall safe "select" c_select :: CInt -> Ptr CFdSet -> Ptr CFdSet -> Ptr CFdSet -> Ptr CTimeVal @@ -1226,12 +1246,6 @@ foreign import ccall unsafe "hsFD_SETSIZE" fD_SETSIZE :: Fd fD_SETSIZE = fromIntegral c_fD_SETSIZE -foreign import ccall unsafe "hsFD_CLR" - c_fdClr :: CInt -> Ptr CFdSet -> IO () - -fdClr :: Fd -> Ptr CFdSet -> IO () -fdClr (Fd fd) fdset = c_fdClr fd fdset - foreign import ccall unsafe "hsFD_ISSET" c_fdIsSet :: CInt -> Ptr CFdSet -> IO CInt @@ -1252,4 +1266,44 @@ foreign import ccall unsafe "sizeof_fd_set" #endif +reportStackOverflow :: IO a +reportStackOverflow = do callStackOverflowHook; return undefined + +reportError :: SomeException -> IO a +reportError ex = do + handler <- getUncaughtExceptionHandler + handler ex + return undefined + +-- SUP: Are the hooks allowed to re-enter Haskell land? If so, remove +-- the unsafe below. +foreign import ccall unsafe "stackOverflow" + callStackOverflowHook :: IO () + +{-# NOINLINE uncaughtExceptionHandler #-} +uncaughtExceptionHandler :: IORef (SomeException -> IO ()) +uncaughtExceptionHandler = unsafePerformIO (newIORef defaultHandler) + where + defaultHandler :: SomeException -> IO () + defaultHandler se@(SomeException ex) = do + (hFlush stdout) `catchAny` (\ _ -> return ()) + let msg = case cast ex of + Just Deadlock -> "no threads to run: infinite loop or deadlock?" + _ -> case cast ex of + Just (ErrorCall s) -> s + _ -> showsPrec 0 se "" + withCString "%s" $ \cfmt -> + withCString msg $ \cmsg -> + errorBelch cfmt cmsg + +-- don't use errorBelch() directly, because we cannot call varargs functions +-- using the FFI. +foreign import ccall unsafe "HsBase.h errorBelch2" + errorBelch :: CString -> CString -> IO () + +setUncaughtExceptionHandler :: (SomeException -> IO ()) -> IO () +setUncaughtExceptionHandler = writeIORef uncaughtExceptionHandler + +getUncaughtExceptionHandler :: IO (SomeException -> IO ()) +getUncaughtExceptionHandler = readIORef uncaughtExceptionHandler \end{code}