summaryrefslogtreecommitdiff
path: root/dht/src/Control/Concurrent/Delay.hs
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2019-12-04 17:50:44 -0500
committerJoe Crayne <joe@jerkface.net>2020-01-01 23:22:52 -0500
commitd6dc5ae72b8fd18c8d2e4f72c59dc6f93d635bc7 (patch)
treec7d4c8d5c9c33b4055e477b11c9c2b073b040610 /dht/src/Control/Concurrent/Delay.hs
parent1bc58d75f6d2abaecbc7c5d54b609a6329f3363d (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.hs49
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 @@
1module Control.Concurrent.Delay where
2
3import Control.Concurrent
4import Control.Monad
5import Control.Exception ({-evaluate,-}handle,finally,throwIO)
6import Data.Time.Clock (NominalDiffTime)
7import System.IO.Error
8
9type Microseconds = Int
10
11microseconds :: NominalDiffTime -> Microseconds
12microseconds d = round $ 1000000 * d
13
14data InterruptibleDelay = InterruptibleDelay
15 { delayThread :: MVar ThreadId
16 }
17
18interruptibleDelay :: IO InterruptibleDelay
19interruptibleDelay = 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.
28startDelay :: InterruptibleDelay -> Microseconds -> IO Bool
29startDelay 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.
45interruptDelay :: InterruptibleDelay -> IO ()
46interruptDelay d = do
47 mthread <- tryTakeMVar (delayThread d)
48 forM_ mthread $ \thread -> do
49 throwTo thread (userError "Interrupted delay")