\begin{code}
#include "Typeable.h"
module GHC.Conc
( ThreadId(..)
, forkIO
, forkOnIO
, numCapabilities
, childHandler
, myThreadId
, killThread
, throwTo
, par
, pseq
, yield
, labelThread
, ThreadStatus(..), BlockReason(..)
, threadStatus
, threadDelay
, registerDelay
, threadWaitRead
, threadWaitWrite
, MVar(..)
, newMVar
, newEmptyMVar
, takeMVar
, putMVar
, tryTakeMVar
, tryPutMVar
, isEmptyMVar
, addMVarFinalizer
, STM(..)
, atomically
, retry
, orElse
, catchSTM
, alwaysSucceeds
, always
, TVar(..)
, newTVar
, newTVarIO
, readTVar
, writeTVar
, unsafeIOToSTM
#ifdef mingw32_HOST_OS
, asyncRead
, asyncWrite
, asyncDoProc
, asyncReadBA
, asyncWriteBA
#endif
#ifndef mingw32_HOST_OS
, Signal, HandlerFun, setHandler, runHandlers
#endif
, ensureIOManagerIsRunning
#ifndef mingw32_HOST_OS
, syncIOManager
#endif
#ifdef mingw32_HOST_OS
, ConsoleEvent(..)
, win32ConsoleHandler
, toWin32ConsoleEvent
#endif
, setUncaughtExceptionHandler
, getUncaughtExceptionHandler
, reportError, reportStackOverflow
) where
import System.Posix.Types
#ifndef mingw32_HOST_OS
import System.Posix.Internals
#endif
import Foreign
import Foreign.C
#ifndef mingw32_HOST_OS
import Data.Dynamic
import Control.Monad
#endif
import Data.Maybe
import GHC.Base
import GHC.Handle
import GHC.IOBase
import GHC.Num ( Num(..) )
import GHC.Real ( fromIntegral )
#ifndef mingw32_HOST_OS
import GHC.Arr ( inRange )
#endif
#ifdef mingw32_HOST_OS
import GHC.Real ( div )
import GHC.Ptr ( plusPtr, 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(..) )
import GHC.STRef
import GHC.Show ( Show(..), showString )
import Data.Typeable
import GHC.Err
infixr 0 `par`, `pseq`
\end{code}
%************************************************************************
%* *
\subsection{@ThreadId@, @par@, and @fork@}
%* *
%************************************************************************
\begin{code}
data ThreadId = ThreadId ThreadId# deriving( Typeable )
instance Show ThreadId where
showsPrec d t =
showString "ThreadId " .
showsPrec d (getThreadId (id2TSO t))
foreign import ccall unsafe "rts_getThreadId" getThreadId :: ThreadId# -> CInt
id2TSO :: ThreadId -> ThreadId#
id2TSO (ThreadId t) = t
foreign import ccall unsafe "cmp_thread" cmp_thread :: ThreadId# -> ThreadId# -> CInt
cmpThread :: ThreadId -> ThreadId -> Ordering
cmpThread t1 t2 =
case cmp_thread (id2TSO t1) (id2TSO t2) of
1 -> LT
0 -> EQ
_ -> GT
instance Eq ThreadId where
t1 == t2 =
case t1 `cmpThread` t2 of
EQ -> True
_ -> False
instance Ord ThreadId where
compare = cmpThread
forkIO :: IO () -> IO ThreadId
forkIO action = IO $ \ s ->
case (fork# action_plus s) of (# s1, tid #) -> (# s1, ThreadId tid #)
where
action_plus = catchException action childHandler
forkOnIO :: Int -> IO () -> IO ThreadId
forkOnIO (I# cpu) action = IO $ \ s ->
case (forkOn# cpu action_plus s) of (# s1, tid #) -> (# s1, ThreadId tid #)
where
action_plus = catchException action childHandler
numCapabilities :: Int
numCapabilities = unsafePerformIO $ do
n <- peek n_capabilities
return (fromIntegral n)
foreign import ccall "&n_capabilities" n_capabilities :: Ptr CInt
childHandler :: SomeException -> IO ()
childHandler err = catchException (real_handler err) childHandler
real_handler :: SomeException -> IO ()
real_handler se@(SomeException ex) =
case cast ex of
Just BlockedOnDeadMVar -> return ()
_ -> case cast ex of
Just BlockedIndefinitely -> return ()
_ -> case cast ex of
Just ThreadKilled -> return ()
_ -> case cast ex of
Just StackOverflow -> reportStackOverflow
_ -> reportError se
killThread :: ThreadId -> IO ()
killThread tid = throwTo tid ThreadKilled
throwTo :: Exception e => ThreadId -> e -> IO ()
throwTo (ThreadId tid) ex = IO $ \ s ->
case (killThread# tid (toException ex) s) of s1 -> (# s1, () #)
myThreadId :: IO ThreadId
myThreadId = IO $ \s ->
case (myThreadId# s) of (# s1, tid #) -> (# s1, ThreadId tid #)
yield :: IO ()
yield = IO $ \s ->
case (yield# s) of s1 -> (# s1, () #)
labelThread :: ThreadId -> String -> IO ()
labelThread (ThreadId t) str = IO $ \ s ->
let ps = packCString# str
adr = byteArrayContents# ps in
case (labelThread# t adr s) of s1 -> (# s1, () #)
pseq :: a -> b -> b
pseq x y = x `seq` lazy y
par :: a -> b -> b
par x y = case (par# x) of { _ -> lazy y }
data BlockReason
= BlockedOnMVar
| BlockedOnBlackHole
| BlockedOnException
| BlockedOnSTM
| BlockedOnForeignCall
| BlockedOnOther
deriving (Eq,Ord,Show)
data ThreadStatus
= ThreadRunning
| ThreadFinished
| ThreadBlocked BlockReason
| ThreadDied
deriving (Eq,Ord,Show)
threadStatus :: ThreadId -> IO ThreadStatus
threadStatus (ThreadId t) = IO $ \s ->
case threadStatus# t s of
(# s', stat #) -> (# s', mk_stat (I# stat) #)
where
mk_stat 0 = ThreadRunning
mk_stat 1 = ThreadBlocked BlockedOnMVar
mk_stat 2 = ThreadBlocked BlockedOnBlackHole
mk_stat 3 = ThreadBlocked BlockedOnException
mk_stat 7 = ThreadBlocked BlockedOnSTM
mk_stat 11 = ThreadBlocked BlockedOnForeignCall
mk_stat 12 = ThreadBlocked BlockedOnForeignCall
mk_stat 16 = ThreadFinished
mk_stat 17 = ThreadDied
mk_stat _ = ThreadBlocked BlockedOnOther
\end{code}
%************************************************************************
%* *
\subsection[stm]{Transactional heap operations}
%* *
%************************************************************************
TVars are shared memory locations which support atomic memory
transactions.
\begin{code}
newtype STM a = STM (State# RealWorld -> (# State# RealWorld, a #))
unSTM :: STM a -> (State# RealWorld -> (# State# RealWorld, a #))
unSTM (STM a) = a
INSTANCE_TYPEABLE1(STM,stmTc,"STM")
instance Functor STM where
fmap f x = x >>= (return . f)
instance Monad STM where
m >> k = thenSTM m k
return x = returnSTM x
m >>= k = bindSTM m k
bindSTM :: STM a -> (a -> STM b) -> STM b
bindSTM (STM m) k = STM ( \s ->
case m s of
(# new_s, a #) -> unSTM (k a) new_s
)
thenSTM :: STM a -> STM b -> STM b
thenSTM (STM m) k = STM ( \s ->
case m s of
(# new_s, _ #) -> unSTM k new_s
)
returnSTM :: a -> STM a
returnSTM x = STM (\s -> (# s, x #))
unsafeIOToSTM :: IO a -> STM a
unsafeIOToSTM (IO m) = STM m
atomically :: STM a -> IO a
atomically (STM m) = IO (\s -> (atomically# m) s )
retry :: STM a
retry = STM $ \s# -> retry# s#
orElse :: STM a -> STM a -> STM a
orElse (STM m) e = STM $ \s -> catchRetry# m (unSTM e) s
catchSTM :: STM a -> (SomeException -> STM a) -> STM a
catchSTM (STM m) k = STM $ \s -> catchSTM# m (\ex -> unSTM (k ex)) s
checkInv :: STM a -> STM ()
checkInv (STM m) = STM (\s -> (check# m) s)
alwaysSucceeds :: STM a -> STM ()
alwaysSucceeds i = do ( do i ; retry ) `orElse` ( return () )
checkInv i
always :: STM Bool -> STM ()
always i = alwaysSucceeds ( do v <- i
if (v) then return () else ( error "Transacional invariant violation" ) )
data TVar a = TVar (TVar# RealWorld a)
INSTANCE_TYPEABLE1(TVar,tvarTc,"TVar")
instance Eq (TVar a) where
(TVar tvar1#) == (TVar tvar2#) = sameTVar# tvar1# tvar2#
newTVar :: a -> STM (TVar a)
newTVar val = STM $ \s1# ->
case newTVar# val s1# of
(# s2#, tvar# #) -> (# s2#, TVar tvar# #)
newTVarIO :: a -> IO (TVar a)
newTVarIO val = IO $ \s1# ->
case newTVar# val s1# of
(# s2#, tvar# #) -> (# s2#, TVar tvar# #)
readTVar :: TVar a -> STM a
readTVar (TVar tvar#) = STM $ \s# -> readTVar# tvar# s#
writeTVar :: TVar a -> a -> STM ()
writeTVar (TVar tvar#) val = STM $ \s1# ->
case writeTVar# tvar# val s1# of
s2# -> (# s2#, () #)
\end{code}
%************************************************************************
%* *
\subsection[mvars]{MStructures}
%* *
%************************************************************************
MVars are rendezvous points for concurrent threads. They begin
empty, and any attempt to read an empty MVar blocks. When an MVar
is written, a single blocked thread may be freed. Reading an MVar
toggles its state from full back to empty. Therefore, any value
written to an MVar may only be read once. Multiple reads and writes
are allowed, but there must be at least one read between any two
writes.
\begin{code}
newEmptyMVar :: IO (MVar a)
newEmptyMVar = IO $ \ s# ->
case newMVar# s# of
(# s2#, svar# #) -> (# s2#, MVar svar# #)
newMVar :: a -> IO (MVar a)
newMVar value =
newEmptyMVar >>= \ mvar ->
putMVar mvar value >>
return mvar
takeMVar :: MVar a -> IO a
takeMVar (MVar mvar#) = IO $ \ s# -> takeMVar# mvar# s#
putMVar :: MVar a -> a -> IO ()
putMVar (MVar mvar#) x = IO $ \ s# ->
case putMVar# mvar# x s# of
s2# -> (# s2#, () #)
tryTakeMVar :: MVar a -> IO (Maybe a)
tryTakeMVar (MVar m) = IO $ \ s ->
case tryTakeMVar# m s of
(# s', 0#, _ #) -> (# s', Nothing #)
(# s', _, a #) -> (# s', Just a #)
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 #)
isEmptyMVar :: MVar a -> IO Bool
isEmptyMVar (MVar mv#) = IO $ \ s# ->
case isEmptyMVar# mv# s# of
(# s2#, flg #) -> (# s2#, not (flg ==# 0#) #)
addMVarFinalizer :: MVar a -> IO () -> IO ()
addMVarFinalizer (MVar m) finalizer =
IO $ \s -> case mkWeak# m () finalizer s of { (# s1, _ #) -> (# s1, () #) }
\end{code}
%************************************************************************
%* *
\subsection{Thread waiting}
%* *
%************************************************************************
\begin{code}
#ifdef mingw32_HOST_OS
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#) #)
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#) #)
asyncDoProc :: FunPtr (Ptr a -> IO Int) -> Ptr a -> IO Int
asyncDoProc (FunPtr proc) (Ptr param) =
IO $ \s -> case asyncDoProc# proc param s of
(# s', _len#, err# #) -> (# s', I# err# #)
asyncReadBA :: Int -> Int -> Int -> Int -> MutableByteArray# RealWorld -> IO (Int,Int)
asyncReadBA fd isSock len off bufB =
asyncRead fd isSock len ((Ptr (byteArrayContents# (unsafeCoerce# bufB))) `plusPtr` off)
asyncWriteBA :: Int -> Int -> Int -> Int -> MutableByteArray# RealWorld -> IO (Int,Int)
asyncWriteBA fd isSock len off bufB =
asyncWrite fd isSock len ((Ptr (byteArrayContents# (unsafeCoerce# bufB))) `plusPtr` off)
#endif
threadWaitRead :: Fd -> IO ()
threadWaitRead fd
#ifndef mingw32_HOST_OS
| threaded = waitForReadEvent fd
#endif
| otherwise = IO $ \s ->
case fromIntegral fd of { I# fd# ->
case waitRead# fd# s of { s' -> (# s', () #)
}}
threadWaitWrite :: Fd -> IO ()
threadWaitWrite fd
#ifndef mingw32_HOST_OS
| threaded = waitForWriteEvent fd
#endif
| otherwise = IO $ \s ->
case fromIntegral fd of { I# fd# ->
case waitWrite# fd# s of { s' -> (# s', () #)
}}
threadDelay :: Int -> IO ()
threadDelay time
| threaded = waitForDelayEvent time
| otherwise = IO $ \s ->
case fromIntegral time of { I# time# ->
case delay# time# s of { s' -> (# s', () #)
}}
registerDelay :: Int -> IO (TVar Bool)
registerDelay usecs
| threaded = waitForDelayEventSTM usecs
| otherwise = error "registerDelay: requires -threaded"
foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool
waitForDelayEvent :: Int -> IO ()
waitForDelayEvent usecs = do
m <- newEmptyMVar
target <- calculateTarget usecs
atomicModifyIORef pendingDelays (\xs -> (Delay target m : xs, ()))
prodServiceThread
takeMVar m
waitForDelayEventSTM :: Int -> IO (TVar Bool)
waitForDelayEventSTM usecs = do
t <- atomically $ newTVar False
target <- calculateTarget usecs
atomicModifyIORef pendingDelays (\xs -> (DelaySTM target t : xs, ()))
prodServiceThread
return t
calculateTarget :: Int -> IO USecs
calculateTarget usecs = do
now <- getUSecOfDay
return $ now + (fromIntegral usecs)
#ifndef mingw32_HOST_OS
data IOReq
= Read !Fd !(MVar ())
| Write !Fd !(MVar ())
#endif
data DelayReq
= Delay !USecs !(MVar ())
| DelaySTM !USecs !(TVar Bool)
#ifndef mingw32_HOST_OS
pendingEvents :: IORef [IOReq]
#endif
pendingDelays :: IORef [DelayReq]
(pendingEvents,pendingDelays) = unsafePerformIO $ do
startIOManagerThread
reqs <- newIORef []
dels <- newIORef []
return (reqs, dels)
ensureIOManagerIsRunning :: IO ()
ensureIOManagerIsRunning
| threaded = seq pendingEvents $ return ()
| otherwise = return ()
insertDelay :: DelayReq -> [DelayReq] -> [DelayReq]
insertDelay d [] = [d]
insertDelay d1 ds@(d2 : rest)
| delayTime d1 <= delayTime d2 = d1 : ds
| otherwise = d2 : insertDelay d1 rest
delayTime :: DelayReq -> USecs
delayTime (Delay t _) = t
delayTime (DelaySTM t _) = t
type USecs = Word64
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
prodding :: IORef Bool
prodding = unsafePerformIO (newIORef False)
prodServiceThread :: IO ()
prodServiceThread = do
was_set <- atomicModifyIORef prodding (\a -> (True,a))
if (not (was_set)) then wakeupIOManager else return ()
#ifdef mingw32_HOST_OS
startIOManagerThread :: IO ()
startIOManagerThread = do
wakeup <- c_getIOManagerEvent
forkIO $ service_loop wakeup []
return ()
service_loop :: HANDLE
-> [DelayReq]
-> IO ()
service_loop wakeup old_delays = do
new_delays <- atomicModifyIORef pendingDelays (\a -> ([],a))
let delays = foldr insertDelay old_delays new_delays
now <- getUSecOfDay
(delays', timeout) <- getDelay now delays
r <- c_WaitForSingleObject wakeup timeout
case r of
0xffffffff -> do c_maperrno; throwErrno "service_loop"
0 -> do
r2 <- c_readIOManagerEvent
exit <-
case r2 of
_ | r2 == io_MANAGER_WAKEUP -> return False
_ | r2 == io_MANAGER_DIE -> return True
0 -> 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'
service_cont :: HANDLE -> [DelayReq] -> IO ()
service_cont wakeup delays = do
r <- atomicModifyIORef prodding (\_ -> (False,False))
r `seq` return ()
service_loop wakeup delays
io_MANAGER_WAKEUP, io_MANAGER_DIE :: Word32
io_MANAGER_WAKEUP = 0xffffffff
io_MANAGER_DIE = 0xfffffffe
data ConsoleEvent
= ControlC
| Break
| Close
| Logoff
| Shutdown
deriving (Eq, Ord, Enum, Show, Read, Typeable)
start_console_handler :: Word32 -> IO ()
start_console_handler r =
case toWin32ConsoleEvent r of
Just x -> withMVar win32ConsoleHandler $ \handler -> do
forkIO (handler x)
return ()
Nothing -> return ()
toWin32ConsoleEvent :: Num a => a -> Maybe ConsoleEvent
toWin32ConsoleEvent ev =
case ev of
0 -> Just ControlC
1 -> Just Break
2 -> Just Close
5 -> Just Logoff
6 -> Just Shutdown
_ -> Nothing
win32ConsoleHandler :: MVar (ConsoleEvent -> IO ())
win32ConsoleHandler = unsafePerformIO (newMVar (error "win32ConsoleHandler"))
stick :: IORef HANDLE
stick = unsafePerformIO (newIORef nullPtr)
wakeupIOManager :: IO ()
wakeupIOManager = do
_hdl <- readIORef stick
c_sendIOManagerEvent io_MANAGER_WAKEUP
getDelay :: USecs -> [DelayReq] -> IO ([DelayReq], DWORD)
getDelay _ [] = return ([], iNFINITE)
getDelay now all@(d : rest)
= case d of
Delay time m | now >= time -> do
putMVar m ()
getDelay now rest
DelaySTM time t | now >= time -> do
atomically $ writeTVar t True
getDelay now rest
_otherwise ->
let micro_seconds = delayTime d now
milli_seconds = (micro_seconds + 999) `div` 1000
in return (all, fromIntegral milli_seconds)
type HANDLE = Ptr ()
type DWORD = Word32
iNFINITE :: DWORD
iNFINITE = 0xFFFFFFFF
foreign import ccall unsafe "getIOManagerEvent"
c_getIOManagerEvent :: IO HANDLE
foreign import ccall unsafe "readIOManagerEvent"
c_readIOManagerEvent :: IO Word32
foreign import ccall unsafe "sendIOManagerEvent"
c_sendIOManagerEvent :: Word32 -> IO ()
foreign import ccall unsafe "maperrno"
c_maperrno :: IO ()
foreign import stdcall "WaitForSingleObject"
c_WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD
#else
startIOManagerThread :: IO ()
startIOManagerThread = do
allocaArray 2 $ \fds -> do
throwErrnoIfMinus1 "startIOManagerThread" (c_pipe fds)
rd_end <- peekElemOff fds 0
wr_end <- peekElemOff fds 1
setNonBlockingFD wr_end
setCloseOnExec rd_end
setCloseOnExec wr_end
writeIORef stick (fromIntegral wr_end)
c_setIOManagerPipe wr_end
forkIO $ do
allocaBytes sizeofFdSet $ \readfds -> do
allocaBytes sizeofFdSet $ \writefds -> do
allocaBytes sizeofTimeVal $ \timeval -> do
service_loop (fromIntegral rd_end) readfds writefds timeval [] []
return ()
service_loop
:: Fd
-> Ptr CFdSet
-> Ptr CFdSet
-> Ptr CTimeVal
-> [IOReq]
-> [DelayReq]
-> IO ()
service_loop wakeup readfds writefds ptimeval old_reqs old_delays = do
new_reqs <- atomicModifyIORef pendingEvents (\a -> ([],a))
let reqs = new_reqs ++ old_reqs
new_delays <- atomicModifyIORef pendingDelays (\a -> ([],a))
let delays0 = foldr insertDelay old_delays new_delays
fdZero readfds
fdZero writefds
fdSet wakeup readfds
maxfd <- buildFdSets 0 readfds writefds reqs
let do_select delays = do
now <- getUSecOfDay
(delays', timeout) <- getDelay now ptimeval delays
res <- c_select (fromIntegral ((max wakeup maxfd)+1)) readfds writefds
nullPtr timeout
if (res == 1)
then do
err <- getErrno
case err of
_ | err == eINTR -> do_select delays'
_ | err == eBADF -> return (True, delays)
_ | otherwise -> throwErrno "select"
else
return (False,delays')
(wakeup_all,delays') <- do_select delays0
exit <-
if wakeup_all then return False
else do
b <- fdIsSet wakeup readfds
if b == 0
then return False
else alloca $ \p -> do
c_read (fromIntegral wakeup) p 1
s <- peek p
case s of
_ | s == io_MANAGER_WAKEUP -> return False
_ | s == io_MANAGER_DIE -> return True
_ | 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
atomicModifyIORef prodding (\_ -> (False,False))
reqs' <- if wakeup_all then do wakeupAll reqs; return []
else completeRequests reqs readfds writefds []
service_loop wakeup readfds writefds ptimeval reqs' delays'
io_MANAGER_WAKEUP, io_MANAGER_DIE, io_MANAGER_SYNC :: CChar
io_MANAGER_WAKEUP = 0xff
io_MANAGER_DIE = 0xfe
io_MANAGER_SYNC = 0xfd
stick :: IORef Fd
stick = unsafePerformIO (newIORef 0)
sync :: IORef [MVar ()]
sync = unsafePerformIO (newIORef [])
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 ()
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 ()
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
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 _ : 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
if b /= 0
then do putMVar m (); completeRequests reqs readfds writefds reqs'
else completeRequests reqs readfds writefds (Read fd m : reqs')
completeRequests (Write fd m : reqs) readfds writefds reqs' = do
b <- fdIsSet fd writefds
if b /= 0
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 _ m : reqs) = do putMVar m (); wakeupAll reqs
wakeupAll (Write _ m : reqs) = do putMVar m (); wakeupAll reqs
waitForReadEvent :: Fd -> IO ()
waitForReadEvent fd = do
m <- newEmptyMVar
atomicModifyIORef pendingEvents (\xs -> (Read fd m : xs, ()))
prodServiceThread
takeMVar m
waitForWriteEvent :: Fd -> IO ()
waitForWriteEvent fd = do
m <- newEmptyMVar
atomicModifyIORef pendingEvents (\xs -> (Write fd m : xs, ()))
prodServiceThread
takeMVar m
getDelay :: USecs -> Ptr CTimeVal -> [DelayReq] -> IO ([DelayReq], Ptr CTimeVal)
getDelay _ _ [] = return ([],nullPtr)
getDelay now ptimeval all@(d : rest)
= case d of
Delay time m | now >= time -> do
putMVar m ()
getDelay now ptimeval rest
DelaySTM time t | now >= time -> do
atomically $ writeTVar t True
getDelay now ptimeval rest
_otherwise -> do
setTimevalTicks ptimeval (delayTime d now)
return (all,ptimeval)
data CTimeVal
foreign import ccall unsafe "sizeofTimeVal"
sizeofTimeVal :: Int
foreign import ccall unsafe "setTimevalTicks"
setTimevalTicks :: Ptr CTimeVal -> USecs -> IO ()
data CFdSet
foreign import ccall safe "select"
c_select :: CInt -> Ptr CFdSet -> Ptr CFdSet -> Ptr CFdSet -> Ptr CTimeVal
-> IO CInt
foreign import ccall unsafe "hsFD_SETSIZE"
c_fD_SETSIZE :: CInt
fD_SETSIZE :: Fd
fD_SETSIZE = fromIntegral c_fD_SETSIZE
foreign import ccall unsafe "hsFD_ISSET"
c_fdIsSet :: CInt -> Ptr CFdSet -> IO CInt
fdIsSet :: Fd -> Ptr CFdSet -> IO CInt
fdIsSet (Fd fd) fdset = c_fdIsSet fd fdset
foreign import ccall unsafe "hsFD_SET"
c_fdSet :: CInt -> Ptr CFdSet -> IO ()
fdSet :: Fd -> Ptr CFdSet -> IO ()
fdSet (Fd fd) fdset = c_fdSet fd fdset
foreign import ccall unsafe "hsFD_ZERO"
fdZero :: Ptr CFdSet -> IO ()
foreign import ccall unsafe "sizeof_fd_set"
sizeofFdSet :: Int
#endif
reportStackOverflow :: IO a
reportStackOverflow = do callStackOverflowHook; return undefined
reportError :: SomeException -> IO a
reportError ex = do
handler <- getUncaughtExceptionHandler
handler ex
return undefined
foreign import ccall unsafe "stackOverflow"
callStackOverflowHook :: IO ()
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
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
withMVar :: MVar a -> (a -> IO b) -> IO b
withMVar m io =
block $ do
a <- takeMVar m
b <- catchAny (unblock (io a))
(\e -> do putMVar m a; throw e)
putMVar m a
return b
\end{code}