summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--InterruptibleDelay.hs28
1 files changed, 18 insertions, 10 deletions
diff --git a/InterruptibleDelay.hs b/InterruptibleDelay.hs
index d59ec8ef..b2683441 100644
--- a/InterruptibleDelay.hs
+++ b/InterruptibleDelay.hs
@@ -2,8 +2,9 @@ module InterruptibleDelay where
2 2
3import Control.Concurrent 3import Control.Concurrent
4import Control.Monad 4import Control.Monad
5import Control.Exception ({-evaluate,-}handle,ErrorCall(..)) 5import Control.Exception ({-evaluate,-}handle,finally,throwIO)
6import Data.Time.Clock (NominalDiffTime) 6import Data.Time.Clock (NominalDiffTime)
7import System.IO.Error
7 8
8type Microseconds = Int 9type Microseconds = Int
9 10
@@ -18,24 +19,31 @@ interruptibleDelay :: IO InterruptibleDelay
18interruptibleDelay = do 19interruptibleDelay = do
19 fmap InterruptibleDelay newEmptyMVar 20 fmap InterruptibleDelay newEmptyMVar
20 21
22-- | Delay for the given number of microseconds and return 'True' if the delay
23-- is not interrupted.
24--
25-- Note: If a thread is already waiting on the given 'InterruptibleDelay'
26-- object, then this will block until it becomes available and only then start
27-- the delay timer.
21startDelay :: InterruptibleDelay -> Microseconds -> IO Bool 28startDelay :: InterruptibleDelay -> Microseconds -> IO Bool
22startDelay d interval = do 29startDelay d interval = do
23 thread <- myThreadId 30 thread <- myThreadId
24 handle (\(ErrorCall _)-> do 31 handle (\e -> do when (not $ isUserError e) (throwIO e)
25 debugNoise $ "delay interrupted" 32 return False) $ do
26 return False) $ do
27 putMVar (delayThread d) thread 33 putMVar (delayThread d) thread
28 threadDelay interval 34 threadDelay interval
29 void $ takeMVar (delayThread d) 35 void $ takeMVar (delayThread d)
30 return True 36 return True
37 -- The following cleanup shouldn't be necessary, but I'm paranoid.
38 `finally` tryTakeMVar (delayThread d)
31 39
32 where debugNoise str = return () 40 where debugNoise str = return ()
33 41
34 42-- | Signal the thread waiting on the given 'InterruptibleDelay' object to
43-- continue even though the timeout has not elapsed. If no thread is waiting,
44-- then this is a no-op.
35interruptDelay :: InterruptibleDelay -> IO () 45interruptDelay :: InterruptibleDelay -> IO ()
36interruptDelay d = do 46interruptDelay d = do
37 mthread <- do 47 mthread <- tryTakeMVar (delayThread d)
38 tryTakeMVar (delayThread d) 48 forM_ mthread $ \thread -> do
39 flip (maybe $ return ()) mthread $ \thread -> do 49 throwTo thread (userError "Interrupted delay")
40 throwTo thread (ErrorCall "Interrupted delay")
41