diff options
author | Joe Crayne <joe@jerkface.net> | 2019-12-04 17:50:44 -0500 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2020-01-01 23:22:52 -0500 |
commit | d6dc5ae72b8fd18c8d2e4f72c59dc6f93d635bc7 (patch) | |
tree | c7d4c8d5c9c33b4055e477b11c9c2b073b040610 /dht/src/Control/Concurrent/Delay.hs | |
parent | 1bc58d75f6d2abaecbc7c5d54b609a6329f3363d (diff) |
InterruptibleDelay.hs -> src/Control/Concurrent/Delay.hs
Diffstat (limited to 'dht/src/Control/Concurrent/Delay.hs')
-rw-r--r-- | dht/src/Control/Concurrent/Delay.hs | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/dht/src/Control/Concurrent/Delay.hs b/dht/src/Control/Concurrent/Delay.hs new file mode 100644 index 00000000..67dcd451 --- /dev/null +++ b/dht/src/Control/Concurrent/Delay.hs | |||
@@ -0,0 +1,49 @@ | |||
1 | module Control.Concurrent.Delay where | ||
2 | |||
3 | import Control.Concurrent | ||
4 | import Control.Monad | ||
5 | import Control.Exception ({-evaluate,-}handle,finally,throwIO) | ||
6 | import Data.Time.Clock (NominalDiffTime) | ||
7 | import System.IO.Error | ||
8 | |||
9 | type Microseconds = Int | ||
10 | |||
11 | microseconds :: NominalDiffTime -> Microseconds | ||
12 | microseconds d = round $ 1000000 * d | ||
13 | |||
14 | data InterruptibleDelay = InterruptibleDelay | ||
15 | { delayThread :: MVar ThreadId | ||
16 | } | ||
17 | |||
18 | interruptibleDelay :: IO InterruptibleDelay | ||
19 | interruptibleDelay = do | ||
20 | fmap InterruptibleDelay newEmptyMVar | ||
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. | ||
28 | startDelay :: InterruptibleDelay -> Microseconds -> IO Bool | ||
29 | startDelay d interval = do | ||
30 | thread <- myThreadId | ||
31 | handle (\e -> do when (not $ isUserError e) (throwIO e) | ||
32 | return False) $ do | ||
33 | putMVar (delayThread d) thread | ||
34 | threadDelay interval | ||
35 | void $ takeMVar (delayThread d) | ||
36 | return True | ||
37 | -- The following cleanup shouldn't be necessary, but I'm paranoid. | ||
38 | `finally` tryTakeMVar (delayThread d) | ||
39 | |||
40 | where debugNoise str = return () | ||
41 | |||
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. | ||
45 | interruptDelay :: InterruptibleDelay -> IO () | ||
46 | interruptDelay d = do | ||
47 | mthread <- tryTakeMVar (delayThread d) | ||
48 | forM_ mthread $ \thread -> do | ||
49 | throwTo thread (userError "Interrupted delay") | ||