base-4.3.0.0: System/Event/Thread.hs
{-# LANGUAGE BangPatterns, ForeignFunctionInterface, NoImplicitPrelude #-}
module System.Event.Thread
(
ensureIOManagerIsRunning
, threadWaitRead
, threadWaitWrite
, threadDelay
, registerDelay
) where
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Maybe (Maybe(..))
import Foreign.Ptr (Ptr)
import GHC.Base
import GHC.Conc.Sync (TVar, ThreadId, ThreadStatus(..), atomically, forkIO,
labelThread, modifyMVar_, newTVar, sharedCAF,
threadStatus, writeTVar)
import GHC.MVar (MVar, newEmptyMVar, newMVar, putMVar, takeMVar)
import System.Event.Manager (Event, EventManager, evtRead, evtWrite, loop,
new, registerFd, unregisterFd_, registerTimeout)
import System.IO.Unsafe (unsafePerformIO)
import System.Posix.Types (Fd)
-- | Suspends the current thread for a given number of microseconds
-- (GHC only).
--
-- There is no guarantee that the thread will be rescheduled promptly
-- when the delay has expired, but the thread will never continue to
-- run /earlier/ than specified.
threadDelay :: Int -> IO ()
threadDelay usecs = do
Just mgr <- readIORef eventManager
m <- newEmptyMVar
_ <- registerTimeout mgr usecs (putMVar m ())
takeMVar m
-- | Set the value of returned TVar to True after a given number of
-- microseconds. The caveats associated with threadDelay also apply.
--
registerDelay :: Int -> IO (TVar Bool)
registerDelay usecs = do
t <- atomically $ newTVar False
Just mgr <- readIORef eventManager
_ <- registerTimeout mgr usecs . atomically $ writeTVar t True
return t
-- | Block the current thread until data is available to read from the
-- given file descriptor.
threadWaitRead :: Fd -> IO ()
threadWaitRead = threadWait evtRead
{-# INLINE threadWaitRead #-}
-- | Block the current thread until the given file descriptor can
-- accept data to write.
threadWaitWrite :: Fd -> IO ()
threadWaitWrite = threadWait evtWrite
{-# INLINE threadWaitWrite #-}
threadWait :: Event -> Fd -> IO ()
threadWait evt fd = do
m <- newEmptyMVar
Just mgr <- readIORef eventManager
_ <- registerFd mgr (\reg _ -> unregisterFd_ mgr reg >> putMVar m ()) fd evt
takeMVar m
foreign import ccall unsafe "getOrSetSystemEventThreadEventManagerStore"
getOrSetSystemEventThreadEventManagerStore :: Ptr a -> IO (Ptr a)
eventManager :: IORef (Maybe EventManager)
eventManager = unsafePerformIO $ do
em <- newIORef Nothing
sharedCAF em getOrSetSystemEventThreadEventManagerStore
{-# NOINLINE eventManager #-}
foreign import ccall unsafe "getOrSetSystemEventThreadIOManagerThreadStore"
getOrSetSystemEventThreadIOManagerThreadStore :: Ptr a -> IO (Ptr a)
{-# NOINLINE ioManager #-}
ioManager :: MVar (Maybe ThreadId)
ioManager = unsafePerformIO $ do
m <- newMVar Nothing
sharedCAF m getOrSetSystemEventThreadIOManagerThreadStore
ensureIOManagerIsRunning :: IO ()
ensureIOManagerIsRunning
| not threaded = return ()
| otherwise = modifyMVar_ ioManager $ \old -> do
let create = do
!mgr <- new
writeIORef eventManager $ Just mgr
!t <- forkIO $ loop mgr
labelThread t "IOManager"
return $ Just t
case old of
Nothing -> create
st@(Just t) -> do
s <- threadStatus t
case s of
ThreadFinished -> create
ThreadDied -> create
_other -> return st
foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool