\begin{code}
#include "Typeable.h"
module GHC.Conc
( ThreadId(..)
, forkIO
, forkOnIO
, numCapabilities
, childHandler
, myThreadId
, killThread
, throwTo
, par
, pseq
, yield
, labelThread
, 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
, signalHandlerLock
#endif
, ensureIOManagerIsRunning
#ifdef mingw32_HOST_OS
, ConsoleEvent(..)
, win32ConsoleHandler
, toWin32ConsoleEvent
#endif
) where
import System.Posix.Types
#ifndef mingw32_HOST_OS
import System.Posix.Internals
#endif
import Foreign
import Foreign.C
#ifndef __HADDOCK__
import GHC.TopHandler ( reportError, reportStackOverflow )
#endif
import Data.Maybe
import GHC.Base
import GHC.IOBase
import GHC.Num ( Num(..) )
import GHC.Real ( fromIntegral, div )
#ifndef mingw32_HOST_OS
import GHC.Base ( Int(..) )
#endif
#ifdef mingw32_HOST_OS
import GHC.Read ( Read )
import GHC.Enum ( Enum )
#endif
import GHC.Exception
import GHC.Pack ( packCString# )
import GHC.Ptr ( Ptr(..), plusPtr, FunPtr(..) )
import GHC.STRef
import GHC.Show ( Show(..), showString )
import Data.Typeable
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, id #) -> (# s1, ThreadId id #)
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, id #) -> (# s1, ThreadId id #)
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 :: Exception -> IO ()
childHandler err = catchException (real_handler err) childHandler
real_handler :: Exception -> IO ()
real_handler ex =
case ex of
BlockedOnDeadMVar -> return ()
BlockedIndefinitely -> return ()
AsyncException ThreadKilled -> return ()
AsyncException StackOverflow -> reportStackOverflow
other -> reportError other
killThread :: ThreadId -> IO ()
killThread tid = throwTo tid (AsyncException ThreadKilled)
throwTo :: ThreadId -> Exception -> IO ()
throwTo (ThreadId id) ex = IO $ \ s ->
case (killThread# id ex s) of s1 -> (# s1, () #)
myThreadId :: IO ThreadId
myThreadId = IO $ \s ->
case (myThreadId# s) of (# s1, id #) -> (# s1, ThreadId id #)
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 }
\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, a #) -> 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 -> (Exception -> 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, w #) -> (# s1, () #) }
withMVar :: MVar a -> (a -> IO b) -> IO b
withMVar m io =
block $ do
a <- takeMVar m
b <- catchException (unblock (io a))
(\e -> do putMVar m a; throw e)
putMVar m a
return b
\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
r <- c_readIOManagerEvent
exit <-
case r of
_ | r == io_MANAGER_WAKEUP -> return False
_ | r == io_MANAGER_DIE -> return True
0 -> return False
r -> do start_console_handler (r `shiftR` 1); return False
if exit
then return ()
else service_cont wakeup delays'
_other -> service_cont wakeup delays'
service_cont wakeup delays = do
atomicModifyIORef prodding (\_ -> (False,False))
service_loop wakeup delays
io_MANAGER_WAKEUP = 0xffffffff :: Word32
io_MANAGER_DIE = 0xfffffffe :: Word32
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 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 = do
hdl <- readIORef stick
c_sendIOManagerEvent io_MANAGER_WAKEUP
getDelay :: USecs -> [DelayReq] -> IO ([DelayReq], DWORD)
getDelay now [] = 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 = 0xFFFFFFFF :: DWORD
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
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 delays = 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 delays
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; return ()
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
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 = 0xff :: CChar
io_MANAGER_DIE = 0xfe :: CChar
stick :: IORef Fd
stick = unsafePerformIO (newIORef 0)
wakeupIOManager :: IO ()
wakeupIOManager = do
fd <- readIORef stick
with io_MANAGER_WAKEUP $ \pbuf -> do
c_write (fromIntegral fd) pbuf 1; return ()
signalHandlerLock :: MVar ()
signalHandlerLock = unsafePerformIO (newMVar ())
foreign import ccall "&signal_handlers" handlers :: Ptr (Ptr (StablePtr (IO ())))
foreign import ccall "setIOManagerPipe"
c_setIOManagerPipe :: CInt -> IO ()
buildFdSets maxfd readfds writefds [] = return maxfd
buildFdSets maxfd readfds writefds (Read fd m : 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)
| fd >= fD_SETSIZE = error "buildFdSets: file descriptor out of range"
| otherwise = do
fdSet fd writefds
buildFdSets (max maxfd fd) readfds writefds reqs
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 [] = return ()
wakeupAll (Read fd m : reqs) = do putMVar m (); wakeupAll reqs
wakeupAll (Write fd 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 now ptimeval [] = 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)
newtype CTimeVal = CTimeVal ()
foreign import ccall unsafe "sizeofTimeVal"
sizeofTimeVal :: Int
foreign import ccall unsafe "setTimevalTicks"
setTimevalTicks :: Ptr CTimeVal -> USecs -> IO ()
newtype CFdSet = 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_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
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
\end{code}