PageRenderTime 89ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/interpreter/ghc/libraries/base/System/Event/Manager.hs

https://github.com/khskrede/mehh
Haskell | 402 lines | 262 code | 50 blank | 90 comment | 6 complexity | 1e3f09cdf0706cc44ee6016ed461ddcd MD5 | raw file
  1. {-# LANGUAGE BangPatterns, CPP, ExistentialQuantification, NoImplicitPrelude,
  2. RecordWildCards, TypeSynonymInstances #-}
  3. module System.Event.Manager
  4. ( -- * Types
  5. EventManager
  6. -- * Creation
  7. , new
  8. , newWith
  9. , newDefaultBackend
  10. -- * Running
  11. , finished
  12. , loop
  13. , step
  14. , shutdown
  15. , cleanup
  16. , wakeManager
  17. -- * Registering interest in I/O events
  18. , Event
  19. , evtRead
  20. , evtWrite
  21. , IOCallback
  22. , FdKey(keyFd)
  23. , registerFd_
  24. , registerFd
  25. , unregisterFd_
  26. , unregisterFd
  27. , closeFd
  28. -- * Registering interest in timeout events
  29. , TimeoutCallback
  30. , TimeoutKey
  31. , registerTimeout
  32. , updateTimeout
  33. , unregisterTimeout
  34. ) where
  35. #include "EventConfig.h"
  36. ------------------------------------------------------------------------
  37. -- Imports
  38. import Control.Concurrent.MVar (MVar, modifyMVar, newMVar, readMVar)
  39. import Control.Exception (finally)
  40. import Control.Monad ((=<<), forM_, liftM, sequence_, when)
  41. import Data.IORef (IORef, atomicModifyIORef, mkWeakIORef, newIORef, readIORef,
  42. writeIORef)
  43. import Data.Maybe (Maybe(..))
  44. import Data.Monoid (mappend, mconcat, mempty)
  45. import GHC.Base
  46. import GHC.Conc.Signal (runHandlers)
  47. import GHC.List (filter)
  48. import GHC.Num (Num(..))
  49. import GHC.Real ((/), fromIntegral )
  50. import GHC.Show (Show(..))
  51. import System.Event.Clock (getCurrentTime)
  52. import System.Event.Control
  53. import System.Event.Internal (Backend, Event, evtClose, evtRead, evtWrite,
  54. Timeout(..))
  55. import System.Event.Unique (Unique, UniqueSource, newSource, newUnique)
  56. import System.Posix.Types (Fd)
  57. import qualified System.Event.IntMap as IM
  58. import qualified System.Event.Internal as I
  59. import qualified System.Event.PSQ as Q
  60. #if defined(HAVE_KQUEUE)
  61. import qualified System.Event.KQueue as KQueue
  62. #elif defined(HAVE_EPOLL)
  63. import qualified System.Event.EPoll as EPoll
  64. #elif defined(HAVE_POLL)
  65. import qualified System.Event.Poll as Poll
  66. #else
  67. # error not implemented for this operating system
  68. #endif
  69. ------------------------------------------------------------------------
  70. -- Types
  71. data FdData = FdData {
  72. fdKey :: {-# UNPACK #-} !FdKey
  73. , fdEvents :: {-# UNPACK #-} !Event
  74. , _fdCallback :: !IOCallback
  75. } deriving (Show)
  76. -- | A file descriptor registration cookie.
  77. data FdKey = FdKey {
  78. keyFd :: {-# UNPACK #-} !Fd
  79. , keyUnique :: {-# UNPACK #-} !Unique
  80. } deriving (Eq, Show)
  81. -- | Callback invoked on I/O events.
  82. type IOCallback = FdKey -> Event -> IO ()
  83. instance Show IOCallback where
  84. show _ = "IOCallback"
  85. -- | A timeout registration cookie.
  86. newtype TimeoutKey = TK Unique
  87. deriving (Eq)
  88. -- | Callback invoked on timeout events.
  89. type TimeoutCallback = IO ()
  90. data State = Created
  91. | Running
  92. | Dying
  93. | Finished
  94. deriving (Eq, Show)
  95. -- | A priority search queue, with timeouts as priorities.
  96. type TimeoutQueue = Q.PSQ TimeoutCallback
  97. {-
  98. Instead of directly modifying the 'TimeoutQueue' in
  99. e.g. 'registerTimeout' we keep a list of edits to perform, in the form
  100. of a chain of function closures, and have the I/O manager thread
  101. perform the edits later. This exist to address the following GC
  102. problem:
  103. Since e.g. 'registerTimeout' doesn't force the evaluation of the
  104. thunks inside the 'emTimeouts' IORef a number of thunks build up
  105. inside the IORef. If the I/O manager thread doesn't evaluate these
  106. thunks soon enough they'll get promoted to the old generation and
  107. become roots for all subsequent minor GCs.
  108. When the thunks eventually get evaluated they will each create a new
  109. intermediate 'TimeoutQueue' that immediately becomes garbage. Since
  110. the thunks serve as roots until the next major GC these intermediate
  111. 'TimeoutQueue's will get copied unnecesarily in the next minor GC,
  112. increasing GC time. This problem is known as "floating garbage".
  113. Keeping a list of edits doesn't stop this from happening but makes the
  114. amount of data that gets copied smaller.
  115. TODO: Evaluate the content of the IORef to WHNF on each insert once
  116. this bug is resolved: http://hackage.haskell.org/trac/ghc/ticket/3838
  117. -}
  118. -- | An edit to apply to a 'TimeoutQueue'.
  119. type TimeoutEdit = TimeoutQueue -> TimeoutQueue
  120. -- | The event manager state.
  121. data EventManager = EventManager
  122. { emBackend :: !Backend
  123. , emFds :: {-# UNPACK #-} !(MVar (IM.IntMap [FdData]))
  124. , emTimeouts :: {-# UNPACK #-} !(IORef TimeoutEdit)
  125. , emState :: {-# UNPACK #-} !(IORef State)
  126. , emUniqueSource :: {-# UNPACK #-} !UniqueSource
  127. , emControl :: {-# UNPACK #-} !Control
  128. }
  129. ------------------------------------------------------------------------
  130. -- Creation
  131. handleControlEvent :: EventManager -> FdKey -> Event -> IO ()
  132. handleControlEvent mgr reg _evt = do
  133. msg <- readControlMessage (emControl mgr) (keyFd reg)
  134. case msg of
  135. CMsgWakeup -> return ()
  136. CMsgDie -> writeIORef (emState mgr) Finished
  137. CMsgSignal fp s -> runHandlers fp s
  138. newDefaultBackend :: IO Backend
  139. #if defined(HAVE_KQUEUE)
  140. newDefaultBackend = KQueue.new
  141. #elif defined(HAVE_EPOLL)
  142. newDefaultBackend = EPoll.new
  143. #elif defined(HAVE_POLL)
  144. newDefaultBackend = Poll.new
  145. #else
  146. newDefaultBackend = error "no back end for this platform"
  147. #endif
  148. -- | Create a new event manager.
  149. new :: IO EventManager
  150. new = newWith =<< newDefaultBackend
  151. newWith :: Backend -> IO EventManager
  152. newWith be = do
  153. iofds <- newMVar IM.empty
  154. timeouts <- newIORef id
  155. ctrl <- newControl
  156. state <- newIORef Created
  157. us <- newSource
  158. _ <- mkWeakIORef state $ do
  159. st <- atomicModifyIORef state $ \s -> (Finished, s)
  160. when (st /= Finished) $ do
  161. I.delete be
  162. closeControl ctrl
  163. let mgr = EventManager { emBackend = be
  164. , emFds = iofds
  165. , emTimeouts = timeouts
  166. , emState = state
  167. , emUniqueSource = us
  168. , emControl = ctrl
  169. }
  170. _ <- registerFd_ mgr (handleControlEvent mgr) (controlReadFd ctrl) evtRead
  171. _ <- registerFd_ mgr (handleControlEvent mgr) (wakeupReadFd ctrl) evtRead
  172. return mgr
  173. -- | Asynchronously shuts down the event manager, if running.
  174. shutdown :: EventManager -> IO ()
  175. shutdown mgr = do
  176. state <- atomicModifyIORef (emState mgr) $ \s -> (Dying, s)
  177. when (state == Running) $ sendDie (emControl mgr)
  178. finished :: EventManager -> IO Bool
  179. finished mgr = (== Finished) `liftM` readIORef (emState mgr)
  180. cleanup :: EventManager -> IO ()
  181. cleanup EventManager{..} = do
  182. writeIORef emState Finished
  183. I.delete emBackend
  184. closeControl emControl
  185. ------------------------------------------------------------------------
  186. -- Event loop
  187. -- | Start handling events. This function loops until told to stop,
  188. -- using 'shutdown'.
  189. --
  190. -- /Note/: This loop can only be run once per 'EventManager', as it
  191. -- closes all of its control resources when it finishes.
  192. loop :: EventManager -> IO ()
  193. loop mgr@EventManager{..} = do
  194. state <- atomicModifyIORef emState $ \s -> case s of
  195. Created -> (Running, s)
  196. _ -> (s, s)
  197. case state of
  198. Created -> go Q.empty `finally` cleanup mgr
  199. Dying -> cleanup mgr
  200. _ -> do cleanup mgr
  201. error $ "System.Event.Manager.loop: state is already " ++
  202. show state
  203. where
  204. go q = do (running, q') <- step mgr q
  205. when running $ go q'
  206. step :: EventManager -> TimeoutQueue -> IO (Bool, TimeoutQueue)
  207. step mgr@EventManager{..} tq = do
  208. (timeout, q') <- mkTimeout tq
  209. I.poll emBackend timeout (onFdEvent mgr)
  210. state <- readIORef emState
  211. state `seq` return (state == Running, q')
  212. where
  213. -- | Call all expired timer callbacks and return the time to the
  214. -- next timeout.
  215. mkTimeout :: TimeoutQueue -> IO (Timeout, TimeoutQueue)
  216. mkTimeout q = do
  217. now <- getCurrentTime
  218. applyEdits <- atomicModifyIORef emTimeouts $ \f -> (id, f)
  219. let (expired, q'') = let q' = applyEdits q in q' `seq` Q.atMost now q'
  220. sequence_ $ map Q.value expired
  221. let timeout = case Q.minView q'' of
  222. Nothing -> Forever
  223. Just (Q.E _ t _, _) ->
  224. -- This value will always be positive since the call
  225. -- to 'atMost' above removed any timeouts <= 'now'
  226. let t' = t - now in t' `seq` Timeout t'
  227. return (timeout, q'')
  228. ------------------------------------------------------------------------
  229. -- Registering interest in I/O events
  230. -- | Register interest in the given events, without waking the event
  231. -- manager thread. The 'Bool' return value indicates whether the
  232. -- event manager ought to be woken.
  233. registerFd_ :: EventManager -> IOCallback -> Fd -> Event
  234. -> IO (FdKey, Bool)
  235. registerFd_ EventManager{..} cb fd evs = do
  236. u <- newUnique emUniqueSource
  237. modifyMVar emFds $ \oldMap -> do
  238. let fd' = fromIntegral fd
  239. reg = FdKey fd u
  240. !fdd = FdData reg evs cb
  241. (!newMap, (oldEvs, newEvs)) =
  242. case IM.insertWith (++) fd' [fdd] oldMap of
  243. (Nothing, n) -> (n, (mempty, evs))
  244. (Just prev, n) -> (n, pairEvents prev newMap fd')
  245. modify = oldEvs /= newEvs
  246. when modify $ I.modifyFd emBackend fd oldEvs newEvs
  247. return (newMap, (reg, modify))
  248. {-# INLINE registerFd_ #-}
  249. -- | @registerFd mgr cb fd evs@ registers interest in the events @evs@
  250. -- on the file descriptor @fd@. @cb@ is called for each event that
  251. -- occurs. Returns a cookie that can be handed to 'unregisterFd'.
  252. registerFd :: EventManager -> IOCallback -> Fd -> Event -> IO FdKey
  253. registerFd mgr cb fd evs = do
  254. (r, wake) <- registerFd_ mgr cb fd evs
  255. when wake $ wakeManager mgr
  256. return r
  257. {-# INLINE registerFd #-}
  258. -- | Wake up the event manager.
  259. wakeManager :: EventManager -> IO ()
  260. wakeManager mgr = sendWakeup (emControl mgr)
  261. eventsOf :: [FdData] -> Event
  262. eventsOf = mconcat . map fdEvents
  263. pairEvents :: [FdData] -> IM.IntMap [FdData] -> Int -> (Event, Event)
  264. pairEvents prev m fd = let l = eventsOf prev
  265. r = case IM.lookup fd m of
  266. Nothing -> mempty
  267. Just fds -> eventsOf fds
  268. in (l, r)
  269. -- | Drop a previous file descriptor registration, without waking the
  270. -- event manager thread. The return value indicates whether the event
  271. -- manager ought to be woken.
  272. unregisterFd_ :: EventManager -> FdKey -> IO Bool
  273. unregisterFd_ EventManager{..} (FdKey fd u) =
  274. modifyMVar emFds $ \oldMap -> do
  275. let dropReg cbs = case filter ((/= u) . keyUnique . fdKey) cbs of
  276. [] -> Nothing
  277. cbs' -> Just cbs'
  278. fd' = fromIntegral fd
  279. (!newMap, (oldEvs, newEvs)) =
  280. case IM.updateWith dropReg fd' oldMap of
  281. (Nothing, _) -> (oldMap, (mempty, mempty))
  282. (Just prev, newm) -> (newm, pairEvents prev newm fd')
  283. modify = oldEvs /= newEvs
  284. when modify $ I.modifyFd emBackend fd oldEvs newEvs
  285. return (newMap, modify)
  286. -- | Drop a previous file descriptor registration.
  287. unregisterFd :: EventManager -> FdKey -> IO ()
  288. unregisterFd mgr reg = do
  289. wake <- unregisterFd_ mgr reg
  290. when wake $ wakeManager mgr
  291. -- | Close a file descriptor in a race-safe way.
  292. closeFd :: EventManager -> (Fd -> IO ()) -> Fd -> IO ()
  293. closeFd mgr close fd = do
  294. fds <- modifyMVar (emFds mgr) $ \oldMap -> do
  295. close fd
  296. case IM.delete (fromIntegral fd) oldMap of
  297. (Nothing, _) -> return (oldMap, [])
  298. (Just fds, !newMap) -> do
  299. when (eventsOf fds /= mempty) $ wakeManager mgr
  300. return (newMap, fds)
  301. forM_ fds $ \(FdData reg ev cb) -> cb reg (ev `mappend` evtClose)
  302. ------------------------------------------------------------------------
  303. -- Registering interest in timeout events
  304. -- | Register a timeout in the given number of microseconds. The
  305. -- returned 'TimeoutKey' can be used to later unregister or update the
  306. -- timeout. The timeout is automatically unregistered after the given
  307. -- time has passed.
  308. registerTimeout :: EventManager -> Int -> TimeoutCallback -> IO TimeoutKey
  309. registerTimeout mgr us cb = do
  310. !key <- newUnique (emUniqueSource mgr)
  311. if us <= 0 then cb
  312. else do
  313. now <- getCurrentTime
  314. let expTime = fromIntegral us / 1000000.0 + now
  315. -- We intentionally do not evaluate the modified map to WHNF here.
  316. -- Instead, we leave a thunk inside the IORef and defer its
  317. -- evaluation until mkTimeout in the event loop. This is a
  318. -- workaround for a nasty IORef contention problem that causes the
  319. -- thread-delay benchmark to take 20 seconds instead of 0.2.
  320. atomicModifyIORef (emTimeouts mgr) $ \f ->
  321. let f' = (Q.insert key expTime cb) . f in (f', ())
  322. wakeManager mgr
  323. return $ TK key
  324. -- | Unregister an active timeout.
  325. unregisterTimeout :: EventManager -> TimeoutKey -> IO ()
  326. unregisterTimeout mgr (TK key) = do
  327. atomicModifyIORef (emTimeouts mgr) $ \f ->
  328. let f' = (Q.delete key) . f in (f', ())
  329. wakeManager mgr
  330. -- | Update an active timeout to fire in the given number of
  331. -- microseconds.
  332. updateTimeout :: EventManager -> TimeoutKey -> Int -> IO ()
  333. updateTimeout mgr (TK key) us = do
  334. now <- getCurrentTime
  335. let expTime = fromIntegral us / 1000000.0 + now
  336. atomicModifyIORef (emTimeouts mgr) $ \f ->
  337. let f' = (Q.adjust (const expTime) key) . f in (f', ())
  338. wakeManager mgr
  339. ------------------------------------------------------------------------
  340. -- Utilities
  341. -- | Call the callbacks corresponding to the given file descriptor.
  342. onFdEvent :: EventManager -> Fd -> Event -> IO ()
  343. onFdEvent mgr fd evs = do
  344. fds <- readMVar (emFds mgr)
  345. case IM.lookup (fromIntegral fd) fds of
  346. Just cbs -> forM_ cbs $ \(FdData reg ev cb) ->
  347. when (evs `I.eventIs` ev) $ cb reg evs
  348. Nothing -> return ()