summaryrefslogtreecommitdiff
path: root/RealTimeQueue.hs
diff options
context:
space:
mode:
Diffstat (limited to 'RealTimeQueue.hs')
-rw-r--r--RealTimeQueue.hs101
1 files changed, 75 insertions, 26 deletions
diff --git a/RealTimeQueue.hs b/RealTimeQueue.hs
index dc40c84..78314b1 100644
--- a/RealTimeQueue.hs
+++ b/RealTimeQueue.hs
@@ -1,16 +1,88 @@
1module RealTimeQueue where 1module RealTimeQueue (Queue, createQueue, RealTimeQueue.null, scheduleEvent, extractScheduledEvents, scheduleEventIO, runScheduledIO) where
2import BasePrelude hiding ((<>)) 2import BasePrelude hiding ((<>))
3import System.Clock 3import System.Clock
4import Data.IntPSQ as Q 4import Data.IntPSQ as Q
5import Control.Monad.IO.Class 5import Control.Monad.IO.Class
6import Data.Semigroup 6import Data.Semigroup
7 7
8scheduleEvent :: (TimeSpec, event) -> Queue event -> Queue event 8-- The intended interface of this library is mainly the three functions
9scheduleEvent (ts, ev) (Queue q i) = Queue (Q.insert i ts ev q) (i+1) 9-- ''createQueue'', ''scheduleEventIO'' and ''runScheduledIO''.
10
11createQueue :: Queue event
12createQueue = Queue Q.empty 0
13
14-- The IO commands wrap pure code alternatives (which are also exported), using
15-- MonadIO to add management of the clock and the ability to run a user-supplied
16-- handler. The ''Monotonic'' clock type from ''System.Clock'' is used to define
17-- scheduling priorities internally, but these functions expect time to be
18-- specified relative to the present.
19
20scheduleEventIO :: MonadIO m => (TimeSpec, event) -> Queue event -> m (Queue event)
21scheduleEventIO (ts, ev) queue = do
22 now <- liftIO $ getTime Monotonic
23 return $ scheduleEvent (ts + now, ev) queue
24
25runScheduledIO :: MonadIO m => TimeSpec -> QueueRunner m event -> Queue event -> m (Queue event)
26runScheduledIO timeSpan runner queue = do
27 now <- liftIO $ getTime Monotonic
28 let (events, queue') = extractScheduledEvents now timeSpan queue
29 forM_ events $ \(ts, ev) -> runner (ts - now, ev)
30 return queue'
31
32null :: Queue event -> Bool
33null (Queue q _) = Q.null q
34
35
36
37
38-- The queue runner receives a time, relative to the present, at which the event
39-- is scheduled to occur. Thus, it can wait for this amount of time if desired
40-- (or, in the case of ALSA, push the event onto the kernel queue with this
41-- amount of delay).
42
43-- It is possible for this time to be negative, in which case the event was
44-- scheduled to happen in the past.
10 45
46type QueueRunner m event = (TimeSpec, event) -> m ()
47
48
49
50
51-- It is important that the keys are never re-used. Therefore the constructor is
52-- not exported.
53
54-- (Yes, the keys can wrap around, but if your usage is anything remotely like a
55-- FIFO, the earlier keys will be removed before they get reused. In order to
56-- ensure that the keys _never_ wrap, IntPSQ could be replaced with HashPSQ and
57-- the key type made Integer, but then the size of the keys would increase over
58-- time.)
59
60data Queue event = Queue {
61 _intQueue :: IntPSQ TimeSpec event,
62 _nextKey :: Int
63}
11instance Semigroup (Queue event) where 64instance Semigroup (Queue event) where
12 _ <> q = q 65 _ <> q = q
13 66
67
68
69
70
71-- These three functions provide a pure interface to the schedule queue. They
72-- are agnostic as to the definition of time, but the IO versions below expect
73-- that time should always be defined by the ''Monotonic'' clock in
74-- System.Clock.
75
76-- Schedule an event using absolute time.
77scheduleEvent :: (TimeSpec, event) -> Queue event -> Queue event
78scheduleEvent (ts, ev) (Queue q i) = Queue (Q.insert i ts ev q) (i+1)
79
80-- Given an absolute current time and a timespan (relative to current) into the
81-- future, remove and return all events from the queue that are scheduled
82-- earlier than the end of the timespan.
83
84-- The returned events specify their time as relative to the supplied absolute
85-- current time.
14extractScheduledEvents :: TimeSpec -> TimeSpec -> Queue event -> ([(TimeSpec, event)], Queue event) 86extractScheduledEvents :: TimeSpec -> TimeSpec -> Queue event -> ([(TimeSpec, event)], Queue event)
15extractScheduledEvents currentTime timeSpan = getAllScheduled 87extractScheduledEvents currentTime timeSpan = getAllScheduled
16 where 88 where
@@ -27,26 +99,3 @@ extractScheduledEvents currentTime timeSpan = getAllScheduled
27 Nothing -> Nothing 99 Nothing -> Nothing
28 Just (_, ts, _) | ts > currentTime + timeSpan -> Nothing 100 Just (_, ts, _) | ts > currentTime + timeSpan -> Nothing
29 Just (_, ts, ev) -> Just (ts - currentTime, ev) 101 Just (_, ts, ev) -> Just (ts - currentTime, ev)
30
31type QueueRunner m event = TimeSpec -> event -> m ()
32
33scheduleEventIO :: MonadIO m => (TimeSpec, event) -> Queue event -> m (Queue event)
34scheduleEventIO (ts, ev) queue = do
35 now <- liftIO $ getTime Monotonic
36 return $ scheduleEvent (ts + now, ev) queue
37
38runScheduledIO :: MonadIO m => TimeSpec -> QueueRunner m event -> Queue event -> m (Queue event)
39runScheduledIO timeSpan runner queue = do
40 now <- liftIO $ getTime Monotonic
41 let (events, queue') = extractScheduledEvents now timeSpan queue
42 forM_ events $ \(ts, ev) -> runner (ts - now) ev
43 return queue'
44
45
46createQueue :: Queue event
47createQueue = Queue Q.empty 0
48
49data Queue event = Queue {
50 intQueue :: IntPSQ TimeSpec event,
51 nextKey :: Int
52}