diff options
author | joe <joe@jerkface.net> | 2014-02-22 00:38:28 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-02-22 00:38:28 -0500 |
commit | 926f736e2c445348afa0ae02e8199d5866713434 (patch) | |
tree | 28fd0f94d624f272dcef58aa6da708e1d49f7ce8 /Presence/LockedChan.hs | |
parent | 4e4640386c559b8ff58eccf154cb24c092e406ac (diff) |
LockedChan module
Diffstat (limited to 'Presence/LockedChan.hs')
-rw-r--r-- | Presence/LockedChan.hs | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/Presence/LockedChan.hs b/Presence/LockedChan.hs new file mode 100644 index 00000000..e7e951bb --- /dev/null +++ b/Presence/LockedChan.hs | |||
@@ -0,0 +1,72 @@ | |||
1 | {-# LANGUAGE CPP #-} | ||
2 | module LockedChan | ||
3 | ( LockedChan | ||
4 | , cloneLChan | ||
5 | , newLockedChan | ||
6 | , peekLChan | ||
7 | , unlockChan | ||
8 | , writeLChan ) | ||
9 | where | ||
10 | |||
11 | |||
12 | import Control.Monad.STM | ||
13 | import Control.Concurrent.STM | ||
14 | |||
15 | data LockedChan a = LockedChan | ||
16 | { lock :: TVar Bool | ||
17 | , chan :: TChan a | ||
18 | } | ||
19 | |||
20 | unlockChan :: LockedChan a -> IO (TChan a) | ||
21 | unlockChan c = do | ||
22 | waslocked <- atomically $ swapTVar (lock c) False | ||
23 | if waslocked | ||
24 | then return (chan c) | ||
25 | else error "Attempt to read unlocked channel" | ||
26 | |||
27 | writeLChan :: LockedChan a -> a -> STM () | ||
28 | writeLChan c a = writeTChan (chan c) a | ||
29 | |||
30 | peekLChan :: LockedChan a -> STM a | ||
31 | peekLChan c = peekTChan (chan c) | ||
32 | |||
33 | newLockedChan :: STM (LockedChan a) | ||
34 | newLockedChan = do | ||
35 | lock <- newTVar True | ||
36 | chan <- newTChan | ||
37 | return $ LockedChan lock chan | ||
38 | |||
39 | cloneLChan :: LockedChan a -> IO (LockedChan a) | ||
40 | cloneLChan c = do | ||
41 | mchan <- atomically $ do | ||
42 | locked <- readTVar (lock c) | ||
43 | if locked | ||
44 | then fmap Just $ do | ||
45 | c2 <- cloneTChan (chan c) | ||
46 | l2 <- newTVar True | ||
47 | return $ LockedChan l2 c2 | ||
48 | else return Nothing | ||
49 | return $ maybe (error "Attempt to clone unlocked channel") | ||
50 | id | ||
51 | mchan | ||
52 | |||
53 | #if MIN_VERSION_stm(2,4,0) | ||
54 | #else | ||
55 | -- |Clone a 'TChan': similar to dupTChan, but the cloned channel starts with the | ||
56 | -- same content available as the original channel. | ||
57 | -- | ||
58 | -- Terrible inefficient implementation provided to build against older libraries. | ||
59 | cloneTChan :: TChan a -> STM (TChan a) | ||
60 | cloneTChan chan = do | ||
61 | contents <- chanContents' chan | ||
62 | chan2 <- dupTChan chan | ||
63 | mapM_ (writeTChan chan) contents | ||
64 | return chan2 | ||
65 | where | ||
66 | chanContents' chan = do | ||
67 | b <- isEmptyTChan chan | ||
68 | if b then return [] else do | ||
69 | x <- readTChan chan | ||
70 | xs <- chanContents' chan | ||
71 | return (x:xs) | ||
72 | #endif | ||