[commit: ghc] master: Fix dropped event registrations (1c38325)

git at git.haskell.org git at git.haskell.org
Mon Jun 1 14:58:58 UTC 2015


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/1c3832597b3e75456fc61628c4cd289d211c733b/ghc

>---------------------------------------------------------------

commit 1c3832597b3e75456fc61628c4cd289d211c733b
Author: Ben Gamari <ben at smart-cactus.org>
Date:   Mon Jun 1 02:27:30 2015 -0500

    Fix dropped event registrations
    
    D347 introduced a bug wherein the event manager would drop registrations that
    should be retained during processing. This occurs when an fd has multiple
    registrations, not all of which fire, as well as the case of multi-shot
    registrations.
    
    I also do some general house-keeping, try to better document things, and fix a
    bug which could result in unnecessary calls to `epoll_ctl`
    
    Reviewed By: austin
    
    Differential Revision: https://phabricator.haskell.org/D849
    
    GHC Trac Issues: #10317


>---------------------------------------------------------------

1c3832597b3e75456fc61628c4cd289d211c733b
 libraries/base/GHC/Event/Internal.hs |  7 +++++--
 libraries/base/GHC/Event/Manager.hs  | 40 +++++++++++++++++++++++++-----------
 2 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/libraries/base/GHC/Event/Internal.hs b/libraries/base/GHC/Event/Internal.hs
index 3b75c8b..a093352 100644
--- a/libraries/base/GHC/Event/Internal.hs
+++ b/libraries/base/GHC/Event/Internal.hs
@@ -83,10 +83,12 @@ evtConcat :: [Event] -> Event
 evtConcat = foldl' evtCombine evtNothing
 {-# INLINE evtConcat #-}
 
--- | The lifetime of a registration.
+-- | The lifetime of an event registration.
 --
 -- @since 4.8.1.0
-data Lifetime = OneShot | MultiShot
+data Lifetime = OneShot   -- ^ the registration will be active for only one
+                          -- event
+              | MultiShot -- ^ the registration will trigger multiple times
               deriving (Show, Eq)
 
 -- | The longer of two lifetimes.
@@ -95,6 +97,7 @@ elSupremum OneShot OneShot = OneShot
 elSupremum _       _       = MultiShot
 {-# INLINE elSupremum #-}
 
+-- | @mappend@ == @elSupremum@
 instance Monoid Lifetime where
     mempty = OneShot
     mappend = elSupremum
diff --git a/libraries/base/GHC/Event/Manager.hs b/libraries/base/GHC/Event/Manager.hs
index 11b01ad..b674866 100644
--- a/libraries/base/GHC/Event/Manager.hs
+++ b/libraries/base/GHC/Event/Manager.hs
@@ -456,20 +456,35 @@ onFdEvent mgr fd evs
 
   | otherwise = do
     fdds <- withMVar (callbackTableVar mgr fd) $ \tbl ->
-        IT.delete (fromIntegral fd) tbl >>= maybe (return []) selectCallbacks
+        IT.delete (fromIntegral fd) tbl >>= maybe (return []) (selectCallbacks tbl)
     forM_ fdds $ \(FdData reg _ cb) -> cb reg evs
   where
     -- | Here we look through the list of registrations for the fd of interest
-    -- and sort out which match the events that were triggered. We re-arm
-    -- the fd as appropriate and return this subset.
-    selectCallbacks :: [FdData] -> IO [FdData]
-    selectCallbacks fdds = do
-        let matches :: FdData -> Bool
+    -- and sort out which match the events that were triggered. We,
+    --
+    --   1. re-arm the fd as appropriate
+    --   2. reinsert registrations that weren't triggered and multishot
+    --      registrations
+    --   3. return a list containing the callbacks that should be invoked.
+    selectCallbacks :: IntTable [FdData] -> [FdData] -> IO [FdData]
+    selectCallbacks tbl fdds = do
+        let -- figure out which registrations have been triggered
+            matches :: FdData -> Bool
             matches fd' = evs `I.eventIs` I.elEvent (fdEvents fd')
-            (triggered, saved) = partition matches fdds
+            (triggered, notTriggered) = partition matches fdds
+
+            -- sort out which registrations we need to retain
+            isMultishot :: FdData -> Bool
+            isMultishot fd' = I.elLifetime (fdEvents fd') == MultiShot
+            saved = notTriggered ++ filter isMultishot triggered
+
             savedEls = eventsOf saved
             allEls = eventsOf fdds
 
+        -- Reinsert multishot registrations.
+        -- We deleted the table entry for this fd above so we there isn't a preexisting entry
+        _ <- IT.insertWith (\_ _ -> saved) (fromIntegral fd) saved tbl
+
         case I.elLifetime allEls of
           -- we previously armed the fd for multiple shots, no need to rearm
           MultiShot | allEls == savedEls ->
@@ -477,17 +492,18 @@ onFdEvent mgr fd evs
 
           -- either we previously registered for one shot or the
           -- events of interest have changed, we must re-arm
-          _ -> do
+          _ ->
             case I.elLifetime savedEls of
               OneShot | haveOneShot ->
-                -- if there are no saved events there is no need to re-arm
-                unless (OneShot == I.elLifetime (eventsOf triggered)
-                        && mempty == savedEls) $
+                -- if there are no saved events and we registered with one-shot
+                -- semantics then there is no need to re-arm
+                unless (OneShot == I.elLifetime allEls
+                        && mempty == I.elEvent savedEls) $ do
                   void $ I.modifyFdOnce (emBackend mgr) fd (I.elEvent savedEls)
               _ ->
+                -- we need to re-arm with multi-shot semantics
                 void $ I.modifyFd (emBackend mgr) fd
                                   (I.elEvent allEls) (I.elEvent savedEls)
-            return ()
 
         return triggered
 



More information about the ghc-commits mailing list