+++ /dev/null
-{-# LANGUAGE BangPatterns, ForeignFunctionInterface, NoImplicitPrelude #-}
-
-module System.Event.Thread
- (
- ensureIOManagerIsRunning
- , threadWaitRead
- , threadWaitWrite
- , closeFdWith
- , threadDelay
- , registerDelay
- ) where
-
-import Data.IORef (IORef, newIORef, readIORef, writeIORef)
-import Data.Maybe (Maybe(..))
-import Foreign.C.Error (eBADF, errnoToIOError)
-import Foreign.Ptr (Ptr)
-import GHC.Base
-import GHC.Conc.Sync (TVar, ThreadId, ThreadStatus(..), atomically, forkIO,
- labelThread, modifyMVar_, newTVar, sharedCAF,
- threadStatus, writeTVar)
-import GHC.IO (mask_, onException)
-import GHC.IO.Exception (ioError)
-import GHC.MVar (MVar, newEmptyMVar, newMVar, putMVar, takeMVar)
-import System.Event.Internal (eventIs, evtClose)
-import System.Event.Manager (Event, EventManager, evtRead, evtWrite, loop,
- new, registerFd, unregisterFd_, registerTimeout)
-import qualified System.Event.Manager as M
-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 = mask_ $ do
- Just mgr <- readIORef eventManager
- m <- newEmptyMVar
- reg <- registerTimeout mgr usecs (putMVar m ())
- takeMVar m `onException` M.unregisterTimeout mgr reg
-
--- | 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.
---
--- This will throw an 'IOError' if the file descriptor was closed
--- while this thread was blocked. To safely close a file descriptor
--- that has been used with 'threadWaitRead', use 'closeFdWith'.
-threadWaitRead :: Fd -> IO ()
-threadWaitRead = threadWait evtRead
-{-# INLINE threadWaitRead #-}
-
--- | Block the current thread until the given file descriptor can
--- accept data to write.
---
--- This will throw an 'IOError' if the file descriptor was closed
--- while this thread was blocked. To safely close a file descriptor
--- that has been used with 'threadWaitWrite', use 'closeFdWith'.
-threadWaitWrite :: Fd -> IO ()
-threadWaitWrite = threadWait evtWrite
-{-# INLINE threadWaitWrite #-}
-
--- | Close a file descriptor in a concurrency-safe way.
---
--- Any threads that are blocked on the file descriptor via
--- 'threadWaitRead' or 'threadWaitWrite' will be unblocked by having
--- IO exceptions thrown.
-closeFdWith :: (Fd -> IO ()) -- ^ Action that performs the close.
- -> Fd -- ^ File descriptor to close.
- -> IO ()
-closeFdWith close fd = do
- Just mgr <- readIORef eventManager
- M.closeFd mgr close fd
-
-threadWait :: Event -> Fd -> IO ()
-threadWait evt fd = mask_ $ do
- m <- newEmptyMVar
- Just mgr <- readIORef eventManager
- reg <- registerFd mgr (\reg e -> unregisterFd_ mgr reg >> putMVar m e) fd evt
- evt' <- takeMVar m `onException` unregisterFd_ mgr reg
- if evt' `eventIs` evtClose
- then ioError $ errnoToIOError "threadWait" eBADF Nothing Nothing
- else return ()
-
-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 -> do
- -- Sanity check: if the thread has died, there is a chance
- -- that event manager is still alive. This could happend during
- -- the fork, for example. In this case we should clean up
- -- open pipes and everything else related to the event manager.
- -- See #4449
- mem <- readIORef eventManager
- _ <- case mem of
- Nothing -> return ()
- Just em -> M.cleanup em
- create
- _other -> return st
-
-foreign import ccall unsafe "rtsSupportsBoundThreads" threaded :: Bool