diff options
Diffstat (limited to 'src/Data/PacketQueue.hs')
-rw-r--r-- | src/Data/PacketQueue.hs | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/Data/PacketQueue.hs b/src/Data/PacketQueue.hs new file mode 100644 index 00000000..a617d502 --- /dev/null +++ b/src/Data/PacketQueue.hs | |||
@@ -0,0 +1,65 @@ | |||
1 | -- | This module is useful for implementing a lossess protocol on top of a | ||
2 | -- lossy datagram style protocol. It implements a buffer in which packets may | ||
3 | -- be stored out of order, but from which they are extracted in the proper | ||
4 | -- sequence. | ||
5 | {-# LANGUAGE NamedFieldPuns #-} | ||
6 | module Data.PacketQueue | ||
7 | ( PacketQueue | ||
8 | , new | ||
9 | , dequeue | ||
10 | , enqueue | ||
11 | ) where | ||
12 | |||
13 | import Control.Concurrent.STM | ||
14 | import Control.Concurrent.STM.TArray | ||
15 | import Control.Monad | ||
16 | import Data.Word | ||
17 | import Data.Array.MArray | ||
18 | |||
19 | data PacketQueue a = PacketQueue | ||
20 | { pktq :: TArray Word32 (Maybe a) | ||
21 | , seqno :: TVar Word32 | ||
22 | , qsize :: Word32 | ||
23 | } | ||
24 | |||
25 | -- | Create a new PacketQueue. | ||
26 | new :: Word32 -- ^ Capacity of queue. | ||
27 | -> Word32 -- ^ Initial sequence number. | ||
28 | -> STM (PacketQueue a) | ||
29 | new capacity seqstart = do | ||
30 | let cap = if capacity `mod` 2 == 0 then capacity else capacity + 1 | ||
31 | q <- newArray (0,cap - 1) Nothing | ||
32 | seqv <- newTVar seqstart | ||
33 | return PacketQueue | ||
34 | { pktq = q | ||
35 | , seqno = seqv | ||
36 | , qsize = cap | ||
37 | } | ||
38 | |||
39 | -- | Retry until the next expected packet is enqueued. Then return it. | ||
40 | dequeue :: PacketQueue a -> STM a | ||
41 | dequeue PacketQueue { pktq, seqno, qsize } = do | ||
42 | i0 <- readTVar seqno | ||
43 | let i = i0 `mod` qsize | ||
44 | x <- maybe retry return =<< readArray pktq i | ||
45 | writeArray pktq i Nothing | ||
46 | modifyTVar' seqno succ | ||
47 | return x | ||
48 | |||
49 | -- | Enqueue a packet. Packets need not be enqueued in order as long as there | ||
50 | -- is spare capacity in the queue. If there is not, the packet will be | ||
51 | -- silently discarded without blocking. | ||
52 | enqueue :: PacketQueue a -- ^ The packet queue. | ||
53 | -> Word32 -- ^ Sequence number of the packet. | ||
54 | -> a -- ^ The packet. | ||
55 | -> STM () | ||
56 | enqueue PacketQueue{ pktq, seqno, qsize } no x = do | ||
57 | low <- readTVar seqno | ||
58 | let proj = no - low | ||
59 | -- Ignore packet if out of range. | ||
60 | when ( proj < qsize) $ do | ||
61 | let i = no `mod` qsize | ||
62 | writeArray pktq i (Just x) | ||
63 | |||
64 | -- lookup :: PacketQueue a -> Word32 -> STM (Maybe a) | ||
65 | -- lookup PacketQueue{ pktq, seqno, qsize } no = _todo | ||