diff options
Diffstat (limited to 'RealTimeQueue.hs')
-rw-r--r-- | RealTimeQueue.hs | 101 |
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 @@ | |||
1 | module RealTimeQueue where | 1 | module RealTimeQueue (Queue, createQueue, RealTimeQueue.null, scheduleEvent, extractScheduledEvents, scheduleEventIO, runScheduledIO) where |
2 | import BasePrelude hiding ((<>)) | 2 | import BasePrelude hiding ((<>)) |
3 | import System.Clock | 3 | import System.Clock |
4 | import Data.IntPSQ as Q | 4 | import Data.IntPSQ as Q |
5 | import Control.Monad.IO.Class | 5 | import Control.Monad.IO.Class |
6 | import Data.Semigroup | 6 | import Data.Semigroup |
7 | 7 | ||
8 | scheduleEvent :: (TimeSpec, event) -> Queue event -> Queue event | 8 | -- The intended interface of this library is mainly the three functions |
9 | scheduleEvent (ts, ev) (Queue q i) = Queue (Q.insert i ts ev q) (i+1) | 9 | -- ''createQueue'', ''scheduleEventIO'' and ''runScheduledIO''. |
10 | |||
11 | createQueue :: Queue event | ||
12 | createQueue = 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 | |||
20 | scheduleEventIO :: MonadIO m => (TimeSpec, event) -> Queue event -> m (Queue event) | ||
21 | scheduleEventIO (ts, ev) queue = do | ||
22 | now <- liftIO $ getTime Monotonic | ||
23 | return $ scheduleEvent (ts + now, ev) queue | ||
24 | |||
25 | runScheduledIO :: MonadIO m => TimeSpec -> QueueRunner m event -> Queue event -> m (Queue event) | ||
26 | runScheduledIO 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 | |||
32 | null :: Queue event -> Bool | ||
33 | null (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 | ||
46 | type 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 | |||
60 | data Queue event = Queue { | ||
61 | _intQueue :: IntPSQ TimeSpec event, | ||
62 | _nextKey :: Int | ||
63 | } | ||
11 | instance Semigroup (Queue event) where | 64 | instance 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. | ||
77 | scheduleEvent :: (TimeSpec, event) -> Queue event -> Queue event | ||
78 | scheduleEvent (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. | ||
14 | extractScheduledEvents :: TimeSpec -> TimeSpec -> Queue event -> ([(TimeSpec, event)], Queue event) | 86 | extractScheduledEvents :: TimeSpec -> TimeSpec -> Queue event -> ([(TimeSpec, event)], Queue event) |
15 | extractScheduledEvents currentTime timeSpan = getAllScheduled | 87 | extractScheduledEvents 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 | |||
31 | type QueueRunner m event = TimeSpec -> event -> m () | ||
32 | |||
33 | scheduleEventIO :: MonadIO m => (TimeSpec, event) -> Queue event -> m (Queue event) | ||
34 | scheduleEventIO (ts, ev) queue = do | ||
35 | now <- liftIO $ getTime Monotonic | ||
36 | return $ scheduleEvent (ts + now, ev) queue | ||
37 | |||
38 | runScheduledIO :: MonadIO m => TimeSpec -> QueueRunner m event -> Queue event -> m (Queue event) | ||
39 | runScheduledIO 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 | |||
46 | createQueue :: Queue event | ||
47 | createQueue = Queue Q.empty 0 | ||
48 | |||
49 | data Queue event = Queue { | ||
50 | intQueue :: IntPSQ TimeSpec event, | ||
51 | nextKey :: Int | ||
52 | } | ||