\begin{code}
#include "Typeable.h"
module GHC.Conc
( ThreadId(..)
, forkIO
, forkOnIO
, numCapabilities
, childHandler
, myThreadId
, killThread
, throwTo
, par
, pseq
, runSparks
, yield
, labelThread
, ThreadStatus(..), BlockReason(..)
, threadStatus
, threadDelay
, registerDelay
, threadWaitRead
, threadWaitWrite
, STM(..)
, atomically
, retry
, orElse
, catchSTM
, alwaysSucceeds
, always
, TVar(..)
, newTVar
, newTVarIO
, readTVar
, readTVarIO
, writeTVar
, unsafeIOToSTM
, withMVar
#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
#ifdef mingw32_HOST_OS
import Data.Typeable
#endif
#ifndef mingw32_HOST_OS
import Data.Dynamic
#endif
import Control.Monad
import Data.Maybe
import GHC.Base
#ifndef mingw32_HOST_OS
import GHC.Debug
#endif
import GHC.IO.Handle ( hFlush )
import 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 )
#ifndef mingw32_HOST_OS
import GHC.IOArray
import GHC.Arr ( inRange )
#endif
#ifdef mingw32_HOST_OS
import GHC.Real ( div )
import GHC.Ptr
#endif
#ifdef mingw32_HOST_OS
import GHC.Read ( Read )
import GHC.Enum ( Enum )
#endif
import GHC.Pack ( packCString# )
import GHC.Show ( Show(..), showString )
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)
#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
real_handler :: SomeException -> IO ()
real_handler se@(SomeException ex) =
case cast ex of
Just BlockedIndefinitelyOnMVar -> return ()
_ -> case cast ex of
Just BlockedIndefinitelyOnSTM -> 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 }
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
| 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 ( 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# #)
readTVarIO :: TVar a -> IO a
readTVarIO (TVar tvar#) = IO $ \s# -> readTVarIO# tvar# s#
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}
MVar utilities
\begin{code}
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
modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
modifyMVar_ m io =
block $ do
a <- takeMVar m
a' <- catchAny (unblock (io a))
(\e -> do putMVar m a; throw e)
putMVar m a'
return ()
\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]
pendingEvents = unsafePerformIO $ do
m <- newIORef []
sharedCAF m getOrSetGHCConcPendingEventsStore
foreign import ccall unsafe "getOrSetGHCConcPendingEventsStore"
getOrSetGHCConcPendingEventsStore :: Ptr a -> IO (Ptr a)
#endif
pendingDelays :: IORef [DelayReq]
pendingDelays = unsafePerformIO $ do
m <- newIORef []
sharedCAF m getOrSetGHCConcPendingDelaysStore
foreign import ccall unsafe "getOrSetGHCConcPendingDelaysStore"
getOrSetGHCConcPendingDelaysStore :: Ptr a -> IO (Ptr a)
ioManagerThread :: MVar (Maybe ThreadId)
ioManagerThread = unsafePerformIO $ do
m <- newMVar Nothing
sharedCAF m getOrSetGHCConcIOManagerThreadStore
foreign import ccall unsafe "getOrSetGHCConcIOManagerThreadStore"
getOrSetGHCConcIOManagerThreadStore :: Ptr a -> IO (Ptr a)
ensureIOManagerIsRunning :: IO ()
ensureIOManagerIsRunning
| threaded = startIOManagerThread
| otherwise = return ()
startIOManagerThread :: IO ()
startIOManagerThread = do
modifyMVar_ ioManagerThread $ \old -> do
let create = do t <- forkIO ioManager; return (Just t)
case old of
Nothing -> create
Just t -> do
s <- threadStatus t
case s of
ThreadFinished -> create
ThreadDied -> create
_other -> return (Just t)
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
foreign import ccall unsafe "getUSecOfDay"
getUSecOfDay :: IO USecs
prodding :: IORef Bool
prodding = unsafePerformIO $ do
r <- newIORef False
sharedCAF r getOrSetGHCConcProddingStore
foreign import ccall unsafe "getOrSetGHCConcProddingStore"
getOrSetGHCConcProddingStore :: Ptr a -> IO (Ptr a)
prodServiceThread :: IO ()
prodServiceThread = do
was_set <- atomicModifyIORef prodding $ \b -> (True,b)
if (not (was_set)) then wakeupIOManager else return ()
sharedCAF :: a -> (Ptr a -> IO (Ptr a)) -> IO a
sharedCAF a get_or_set =
block $ do
stable_ref <- newStablePtr a
let ref = castPtr (castStablePtrToPtr stable_ref)
ref2 <- get_or_set ref
if ref==ref2
then return a
else do freeStablePtr stable_ref
deRefStablePtr (castPtrToStablePtr (castPtr ref2))
#ifdef mingw32_HOST_OS
ioManager :: IO ()
ioManager = do
wakeup <- c_getIOManagerEvent
service_loop wakeup []
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
unless exit $ 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"))
wakeupIOManager :: IO ()
wakeupIOManager = 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
ioManager :: IO ()
ioManager = do
allocaArray 2 $ \fds -> do
throwErrnoIfMinus1_ "startIOManagerThread" (c_pipe fds)
rd_end <- peekElemOff fds 0
wr_end <- peekElemOff fds 1
setNonBlockingFD wr_end True
setCloseOnExec rd_end
setCloseOnExec wr_end
c_setIOManagerPipe wr_end
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
atomicModifyIORef prodding (\_ -> (False, ()))
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
warnErrnoIfMinus1_ "service_loop" $
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
unless exit $ do
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 :: Word8
io_MANAGER_WAKEUP = 0xff
io_MANAGER_DIE = 0xfe
io_MANAGER_SYNC = 0xfd
sync :: IORef [MVar ()]
sync = unsafePerformIO (newIORef [])
syncIOManager :: IO ()
syncIOManager = do
m <- newEmptyMVar
atomicModifyIORef sync (\old -> (m:old,()))
c_ioManagerSync
takeMVar m
foreign import ccall unsafe "ioManagerSync" c_ioManagerSync :: IO ()
foreign import ccall unsafe "ioManagerWakeup" wakeupIOManager :: IO ()
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 ()
warnErrnoIfMinus1_ :: Num a => String -> IO a -> IO ()
warnErrnoIfMinus1_ what io
= do r <- io
when (r == 1) $ do
errno <- getErrno
str <- strerror errno >>= peekCString
when (r == 1) $
debugErrLn ("Warning: " ++ what ++ " failed: " ++ str)
foreign import ccall unsafe "string.h" strerror :: Errno -> IO (Ptr CChar)
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
sharedCAF m getOrSetGHCConcSignalHandlerStore
foreign import ccall unsafe "getOrSetGHCConcSignalHandlerStore"
getOrSetGHCConcSignalHandlerStore :: 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 "__hscore_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 ()
reportStackOverflow = callStackOverflowHook
reportError :: SomeException -> IO ()
reportError ex = do
handler <- getUncaughtExceptionHandler
handler ex
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
\end{code}