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