diff options
Diffstat (limited to 'InterruptibleDelay.hs')
-rw-r--r-- | InterruptibleDelay.hs | 28 |
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 | ||
3 | import Control.Concurrent | 3 | import Control.Concurrent |
4 | import Control.Monad | 4 | import Control.Monad |
5 | import Control.Exception ({-evaluate,-}handle,ErrorCall(..)) | 5 | import Control.Exception ({-evaluate,-}handle,finally,throwIO) |
6 | import Data.Time.Clock (NominalDiffTime) | 6 | import Data.Time.Clock (NominalDiffTime) |
7 | import System.IO.Error | ||
7 | 8 | ||
8 | type Microseconds = Int | 9 | type Microseconds = Int |
9 | 10 | ||
@@ -18,24 +19,31 @@ interruptibleDelay :: IO InterruptibleDelay | |||
18 | interruptibleDelay = do | 19 | interruptibleDelay = 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. | ||
21 | startDelay :: InterruptibleDelay -> Microseconds -> IO Bool | 28 | startDelay :: InterruptibleDelay -> Microseconds -> IO Bool |
22 | startDelay d interval = do | 29 | startDelay 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. | ||
35 | interruptDelay :: InterruptibleDelay -> IO () | 45 | interruptDelay :: InterruptibleDelay -> IO () |
36 | interruptDelay d = do | 46 | interruptDelay 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 | |||