1 {-# LANGUAGE BangPatterns, ForeignFunctionInterface, NoImplicitPrelude #-}
3 module System.Event.Thread
5 ensureIOManagerIsRunning
13 import Data.IORef (IORef, newIORef, readIORef, writeIORef)
14 import Data.Maybe (Maybe(..))
15 import Foreign.C.Error (eBADF, errnoToIOError)
16 import Foreign.Ptr (Ptr)
18 import GHC.Conc.Sync (TVar, ThreadId, ThreadStatus(..), atomically, forkIO,
19 labelThread, modifyMVar_, newTVar, sharedCAF,
20 threadStatus, writeTVar)
21 import GHC.IO.Exception (ioError)
22 import GHC.MVar (MVar, newEmptyMVar, newMVar, putMVar, takeMVar)
23 import GHC.Real (fromIntegral)
24 import System.Event.Internal (eventIs, evtClose)
25 import System.Event.Manager (Event, EventManager, evtRead, evtWrite, loop,
26 new, registerFd, unregisterFd_, registerTimeout)
27 import qualified System.Event.Manager as M
28 import System.IO.Unsafe (unsafePerformIO)
29 import System.Posix.Types (Fd)
31 -- | Suspends the current thread for a given number of microseconds
34 -- There is no guarantee that the thread will be rescheduled promptly
35 -- when the delay has expired, but the thread will never continue to
36 -- run /earlier/ than specified.
37 threadDelay :: Int -> IO ()
38 threadDelay usecs = do
39 Just mgr <- readIORef eventManager
41 _ <- registerTimeout mgr usecs (putMVar m ())
44 -- | Set the value of returned TVar to True after a given number of
45 -- microseconds. The caveats associated with threadDelay also apply.
47 registerDelay :: Int -> IO (TVar Bool)
48 registerDelay usecs = do
49 t <- atomically $ newTVar False
50 Just mgr <- readIORef eventManager
51 _ <- registerTimeout mgr usecs . atomically $ writeTVar t True
54 -- | Block the current thread until data is available to read from the
55 -- given file descriptor.
57 -- This will throw an 'IOError' if the file descriptor was closed
58 -- while this thread is blocked.
59 threadWaitRead :: Fd -> IO ()
60 threadWaitRead = threadWait evtRead
61 {-# INLINE threadWaitRead #-}
63 -- | Block the current thread until the given file descriptor can
64 -- accept data to write.
66 -- This will throw an 'IOError' if the file descriptor was closed
67 -- while this thread is blocked.
68 threadWaitWrite :: Fd -> IO ()
69 threadWaitWrite = threadWait evtWrite
70 {-# INLINE threadWaitWrite #-}
72 -- | Close a file descriptor in a concurrency-safe way.
74 -- Any threads that are blocked on the file descriptor via
75 -- 'threadWaitRead' or 'threadWaitWrite' will be unblocked by having
76 -- IO exceptions thrown.
77 closeFd :: (Fd -> IO ()) -- ^ Action that performs the close.
78 -> Fd -- ^ File descriptor to close.
81 Just mgr <- readIORef eventManager
82 M.closeFd mgr close fd
84 threadWait :: Event -> Fd -> IO ()
85 threadWait evt fd = do
87 Just mgr <- readIORef eventManager
88 _ <- registerFd mgr (\reg e -> unregisterFd_ mgr reg >> putMVar m e) fd evt
90 if evt' `eventIs` evtClose
91 then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
94 foreign import ccall unsafe "getOrSetSystemEventThreadEventManagerStore"
95 getOrSetSystemEventThreadEventManagerStore :: Ptr a -> IO (Ptr a)
97 eventManager :: IORef (Maybe EventManager)
98 eventManager = unsafePerformIO $ do
99 em <- newIORef Nothing
100 sharedCAF em getOrSetSystemEventThreadEventManagerStore
101 {-# NOINLINE eventManager #-}
103 foreign import ccall unsafe "getOrSetSystemEventThreadIOManagerThreadStore"
104 getOrSetSystemEventThreadIOManagerThreadStore :: Ptr a -> IO (Ptr a)
106 {-# NOINLINE ioManager #-}
107 ioManager :: MVar (Maybe ThreadId)
108 ioManager = unsafePerformIO $ do
110 sharedCAF m getOrSetSystemEventThreadIOManagerThreadStore
112 ensureIOManagerIsRunning :: IO ()
113 ensureIOManagerIsRunning
114 | not threaded = return ()
115 | otherwise = modifyMVar_ ioManager $ \old -> do
118 writeIORef eventManager $ Just mgr
119 !t <- forkIO $ loop mgr
120 labelThread t "IOManager"
127 ThreadFinished -> create
131 foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool