[commit: base] master: Implement faster System.Timeout.timeout for the threaded RTS. (31153f1)

Ian Lynagh igloo at earth.li
Sun Apr 21 21:53:11 CEST 2013


Repository : ssh://darcs.haskell.org//srv/darcs/packages/base

On branch  : master

https://github.com/ghc/packages-base/commit/31153f1e06ad05002d1a199d9c45a36fcbbaf033

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

commit 31153f1e06ad05002d1a199d9c45a36fcbbaf033
Author: Bertram Felgenhauer <int-e at gmx.de>
Date:   Thu Mar 7 02:19:28 2013 +0100

    Implement faster System.Timeout.timeout for the threaded RTS.
    
    The basic idea is to use the Timer Manager rather than spawning an
    auxilliary thread to handle timeouts; this was proposed by Bas van Dijk
    two years ago (#4963), and recently by Herbert Valerio. The key idea
    to make this work is to spawn an auxilliary thread for the delivery
    of the Timeout exception. This idea as well as most of the code is due
    to Akio Takano.

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

 System/Timeout.hs |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/System/Timeout.hs b/System/Timeout.hs
index be44ba3..869b85c 100644
--- a/System/Timeout.hs
+++ b/System/Timeout.hs
@@ -26,12 +26,16 @@ module System.Timeout ( timeout ) where
 
 #ifdef __GLASGOW_HASKELL__
 import Control.Concurrent
+import Control.Concurrent.MVar
+import GHC.Event           (getSystemTimerManager,
+                            registerTimeout, unregisterTimeout)
 import Control.Exception   (Exception(..), handleJust, bracket,
                             uninterruptibleMask_,
                             asyncExceptionToException,
                             asyncExceptionFromException)
 import Data.Typeable
 import Data.Unique         (Unique, newUnique)
+import Control.Monad
 
 -- An internal type that is thrown as a dynamic exception to
 -- interrupt the running IO computation when the timeout has
@@ -84,6 +88,35 @@ timeout :: Int -> IO a -> IO (Maybe a)
 timeout n f
     | n <  0    = fmap Just f
     | n == 0    = return Nothing
+    | rtsSupportsBoundThreads = do
+        -- In the threaded RTS, we use the Timer Manager to delay the
+        -- (fairly expensive) 'forkIO' call until the timeout has expired.
+        --
+        -- An additional thread is required for the actual delivery of
+        -- the Timeout exception because killThread (or another throwTo)
+        -- is the only way to reliably interrupt a throwTo in flight.
+        pid <- myThreadId
+        ex  <- fmap Timeout newUnique
+        tm  <- getSystemTimerManager
+        -- 'lock' synchronizes the timeout handler and the main thread:
+        --  * the main thread can disable the handler by writing to 'lock';
+        --  * the handler communicates the spawned thread's id through 'lock'.
+        -- These two cases are mutually exclusive.
+        lock <- newEmptyMVar
+        let handleTimeout = do
+                v <- isEmptyMVar lock
+                when v $ void $ forkIOWithUnmask $ \unmask -> unmask $ do
+                    v <- tryPutMVar lock =<< myThreadId
+                    when v $ throwTo pid ex
+            cleanupTimeout key = uninterruptibleMask_ $ do
+                v <- tryPutMVar lock undefined
+                if v then unregisterTimeout tm key
+                     else takeMVar lock >>= killThread
+        handleJust (\e -> if e == ex then Just () else Nothing)
+                   (\_ -> return Nothing)
+                   (bracket (registerTimeout tm n handleTimeout)
+                            cleanupTimeout
+                            (\_ -> fmap Just f))
     | otherwise = do
         pid <- myThreadId
         ex  <- fmap Timeout newUnique





More information about the ghc-commits mailing list