-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | Fast concurrent queues with a Chan-like API, and more
--   
--   This library provides implementations of concurrent FIFO queues (for
--   both general boxed and primitive unboxed values) that are fast,
--   perform well under contention, and offer a Chan-like interface. The
--   library may be of limited usefulness outside of x86 architectures
--   where the fetch-and-add instruction is not available.
--   
--   We export several variations of our design; some support additional
--   functionality while others try for lower latency by removing features
--   or making them more restrictive (e.g. in the <tt>Unboxed</tt>
--   variants).
--   
--   <ul>
--   <li><tt>Unagi</tt>: a general-purpose near drop-in replacement for
--   <tt>Chan</tt>.</li>
--   <li><tt>Unagi.Unboxed</tt>: like <tt>Unagi</tt> but specialized for
--   primitive types; this may perform better if a queue grows very
--   large.</li>
--   <li><tt>Unagi.Bounded</tt>: a bounded variant with blocking and
--   non-blocking writes, and other functionality where a notion of the
--   queue's capacity is required.</li>
--   <li><tt>Unagi.NoBlocking</tt>: lowest latency implementations for when
--   blocking reads aren't required.</li>
--   <li><tt>Unagi.NoBlocking.Unboxed</tt>: like <tt>Unagi.NoBlocking</tt>
--   but for primitive types.</li>
--   </ul>
--   
--   Some of these may be deprecated in the future if they are found to
--   provide little performance benefit, or no unique features; you should
--   benchmark and experiment with them for your use cases, and please
--   submit pull requests for additions to the benchmark suite that reflect
--   what you find.
--   
--   Here is an example benchmark measuring the time taken to concurrently
--   write and read 100,000 messages, with work divided amongst increasing
--   number of readers and writers, comparing against the top-performing
--   queues in the standard libraries. The inset graph shows a zoomed-in
--   view on the implementations here.
--   
@package unagi-chan
@version 0.4.1.0

module Control.Concurrent.Chan.Unagi.Unboxed

-- | Create a new channel, returning its write and read ends.
newChan :: UnagiPrim a => IO (InChan a, OutChan a)

-- | The write end of a channel created with <tt>newChan</tt>.
data InChan a

-- | The read end of a channel created with <tt>newChan</tt>.
data OutChan a

-- | Our class of types supporting primitive array operations. Instance
--   method definitions are architecture-dependent.
class (Prim a, Eq a) => UnagiPrim a

-- | When the read and write operations of the underlying <tt>Prim</tt>
--   instances on aligned memory are atomic, this may be set to <tt>Just
--   x</tt> where <tt>x</tt> is some rare (i.e. unlikely to occur
--   frequently in your data) magic value; this might help speed up some
--   <tt>UnagiPrim</tt> operations.
--   
--   Where those <tt>Prim</tt> instance operations are not atomic, this
--   *must* be set to <tt>Nothing</tt>.
atomicUnicorn :: UnagiPrim a => Maybe a

-- | Read an element from the chan, blocking if the chan is empty.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>readChan</tt> the message that the read would have returned is
--   likely to be lost, even when the read is known to be blocked on an
--   empty queue. If you need to handle this scenario, you can use
--   <a>readChanOnException</a>.
readChan :: UnagiPrim a => OutChan a -> IO a

-- | Like <a>readChan</a> but allows recovery of the queue element which
--   would have been read, in the case that an async exception is raised
--   during the read. To be precise exceptions are raised, and the handler
--   run, only when <tt>readChanOnException</tt> is blocking.
--   
--   The second argument is a handler that takes a blocking IO action
--   returning the element, and performs some recovery action. When the
--   handler is called, the passed <tt>IO a</tt> is the only way to access
--   the element.
readChanOnException :: UnagiPrim a => OutChan a -> (IO a -> IO ()) -> IO a

-- | Returns immediately with:
--   
--   <ul>
--   <li>an <tt><a>Element</a> a</tt> future, which returns one unique
--   element when it becomes available via <a>tryRead</a>.</li>
--   <li>a blocking <tt>IO</tt> action that returns the element when it
--   becomes available.</li>
--   </ul>
--   
--   If you're using this function exclusively you might find the
--   implementation in
--   <a>Control.Concurrent.Chan.Unagi.NoBlocking.Unboxed</a> is faster.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>tryReadChan</tt> the message that the read would have returned is
--   likely to be lost, just as it would be when raised directly after this
--   function returns.
tryReadChan :: UnagiPrim a => OutChan a -> IO (Element a, IO a)

-- | An <tt>IO</tt> action that returns a particular enqueued element when
--   and if it becomes available.
--   
--   Each <tt>Element</tt> corresponds to a particular enqueued element,
--   i.e. a returned <tt>Element</tt> always offers the only means to
--   access one particular enqueued item. The value returned by
--   <tt>tryRead</tt> moves monotonically from <tt>Nothing</tt> to <tt>Just
--   a</tt> when and if an element becomes available, and is idempotent at
--   that point.
newtype Element a
Element :: IO (Maybe a) -> Element a
[tryRead] :: Element a -> IO (Maybe a)

-- | Return a lazy list representing the contents of the supplied OutChan,
--   much like System.IO.hGetContents.
getChanContents :: UnagiPrim a => OutChan a -> IO [a]

-- | Write a value to the channel.
writeChan :: UnagiPrim a => InChan a -> a -> IO ()

-- | Write an entire list of items to a chan type. Writes here from
--   multiple threads may be interleaved, and infinite lists are supported.
writeList2Chan :: UnagiPrim a => InChan a -> [a] -> IO ()

-- | Duplicate a chan: the returned <tt>OutChan</tt> begins empty, but data
--   written to the argument <tt>InChan</tt> from then on will be available
--   from both the original <tt>OutChan</tt> and the one returned here,
--   creating a kind of broadcast channel.
dupChan :: InChan a -> IO (OutChan a)

module Control.Concurrent.Chan.Unagi.NoBlocking.Unboxed

-- | Create a new channel, returning its write and read ends.
newChan :: UnagiPrim a => IO (InChan a, OutChan a)

-- | The write end of a channel created with <tt>newChan</tt>.
data InChan a

-- | The read end of a channel created with <tt>newChan</tt>.
data OutChan a

-- | Our class of types supporting primitive array operations. Instance
--   method definitions are architecture-dependent.
class (Prim a, Eq a) => UnagiPrim a

-- | When the read and write operations of the underlying <tt>Prim</tt>
--   instances on aligned memory are atomic, this may be set to <tt>Just
--   x</tt> where <tt>x</tt> is some rare (i.e. unlikely to occur
--   frequently in your data) magic value; this might help speed up some
--   <tt>UnagiPrim</tt> operations.
--   
--   Where those <tt>Prim</tt> instance operations are not atomic, this
--   *must* be set to <tt>Nothing</tt>.
atomicUnicorn :: UnagiPrim a => Maybe a

-- | Returns immediately with an <tt><a>Element</a> a</tt> future, which
--   returns one unique element when it becomes available via
--   <a>tryRead</a>.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>tryReadChan</tt> the message that the read would have returned is
--   likely to be lost, just as it would be when raised directly after this
--   function returns.
tryReadChan :: UnagiPrim a => OutChan a -> IO (Element a)

-- | <tt>readChan io c</tt> returns the next element from <tt>c</tt>,
--   calling <a>tryReadChan</a> and looping on the <a>Element</a> returned,
--   and calling <tt>io</tt> at each iteration when the element is not yet
--   available. It throws <a>BlockedIndefinitelyOnMVar</a> when
--   <a>isActive</a> determines that a value will never be returned.
--   
--   When used like <tt>readChan <tt>yield</tt></tt> or <tt>readChan
--   (<tt>threadDelay</tt> 10)</tt> this is the semantic equivalent to the
--   blocking <tt>readChan</tt> in the other implementations.
readChan :: UnagiPrim a => IO () -> OutChan a -> IO a

-- | An <tt>IO</tt> action that returns a particular enqueued element when
--   and if it becomes available.
--   
--   Each <tt>Element</tt> corresponds to a particular enqueued element,
--   i.e. a returned <tt>Element</tt> always offers the only means to
--   access one particular enqueued item. The value returned by
--   <tt>tryRead</tt> moves monotonically from <tt>Nothing</tt> to <tt>Just
--   a</tt> when and if an element becomes available, and is idempotent at
--   that point.
newtype Element a
Element :: IO (Maybe a) -> Element a
[tryRead] :: Element a -> IO (Maybe a)

-- | An action that returns <tt>False</tt> sometime after the chan no
--   longer has any writers.
--   
--   After <tt>False</tt> is returned, any <a>tryRead</a> which returns
--   <tt>Nothing</tt> can be considered to be dead. Likewise for
--   <a>tryReadNext</a>. Note that in the blocking implementations a
--   <tt>BlockedIndefinitelyOnMVar</tt> exception is raised, so this
--   function is unnecessary.
isActive :: OutChan a -> IO Bool

-- | Write a value to the channel.
writeChan :: UnagiPrim a => InChan a -> a -> IO ()

-- | Write an entire list of items to a chan type. Writes here from
--   multiple threads may be interleaved, and infinite lists are supported.
writeList2Chan :: UnagiPrim a => InChan a -> [a] -> IO ()

-- | Duplicate a chan: the returned <tt>OutChan</tt> begins empty, but data
--   written to the argument <tt>InChan</tt> from then on will be available
--   from both the original <tt>OutChan</tt> and the one returned here,
--   creating a kind of broadcast channel.
--   
--   See also <a>streamChan</a> for a faster alternative that might be
--   appropriate.
dupChan :: InChan a -> IO (OutChan a)

-- | An infinite stream of elements. <a>tryReadNext</a> can be called any
--   number of times from multiple threads, and returns a value which moves
--   monotonically from <a>Pending</a> to <a>Next</a> if and when a head
--   element becomes available. <tt>isActive</tt> can be used to determine
--   if the stream has expired.
newtype Stream a
Stream :: IO (Next a) -> Stream a
[tryReadNext] :: Stream a -> IO (Next a)
data Next a

-- | The next head element along with the tail <tt>Stream</tt>.
Next :: a -> (Stream a) -> Next a

-- | The next element is not yet in the queue; you can retry
--   <a>tryReadNext</a> until a <tt>Next</tt> is returned.
Pending :: Next a

-- | Produce the specified number of interleaved "streams" from a chan.
--   Nextuming a <a>Stream</a> is much faster than calling
--   <a>tryReadChan</a>, and might be useful when an MPSC queue is needed,
--   or when multiple consumers should be load-balanced in a round-robin
--   fashion.
--   
--   Usage example:
--   
--   <pre>
--   do mapM_ (<a>writeChan</a> i) [1..9]
--      [str1, str2, str2] &lt;- <a>streamChan</a> 3 o
--      forkIO $ printStream str1   -- prints: 1,4,7
--      forkIO $ printStream str2   -- prints: 2,5,8
--      forkIO $ printStream str3   -- prints: 3,6,9
--    where 
--      printStream str = do
--        h &lt;- <a>tryReadNext</a> str
--        case h of
--          <a>Next</a> a str' -&gt; print a &gt;&gt; printStream str'
--          -- We know that all values were already written, so a Pending tells 
--          -- us we can exit; in other cases we might call <tt>yield</tt> and then 
--          -- retry that same <tt><a>tryReadNext</a> str</tt>:
--          <a>Pending</a> -&gt; return ()
--   </pre>
--   
--   Be aware: if one stream consumer falls behind another (e.g. because it
--   is slower) the number of elements in the queue which can't be GC'd
--   will grow. You may want to do some coordination of <a>Stream</a>
--   consumers to prevent this.
streamChan :: UnagiPrim a => Int -> OutChan a -> IO [Stream a]

module Control.Concurrent.Chan.Unagi.NoBlocking

-- | Create a new channel, returning its write and read ends.
newChan :: IO (InChan a, OutChan a)

-- | The write end of a channel created with <tt>newChan</tt>.
data InChan a

-- | The read end of a channel created with <tt>newChan</tt>.
data OutChan a

-- | Returns immediately with an <tt><a>Element</a> a</tt> future, which
--   returns one unique element when it becomes available via
--   <a>tryRead</a>.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>tryReadChan</tt> the message that the read would have returned is
--   likely to be lost, just as it would be when raised directly after this
--   function returns.
tryReadChan :: OutChan a -> IO (Element a)

-- | <tt>readChan io c</tt> returns the next element from <tt>c</tt>,
--   calling <a>tryReadChan</a> and looping on the <a>Element</a> returned,
--   and calling <tt>io</tt> at each iteration when the element is not yet
--   available. It throws <a>BlockedIndefinitelyOnMVar</a> when
--   <a>isActive</a> determines that a value will never be returned.
--   
--   When used like <tt>readChan <tt>yield</tt></tt> or <tt>readChan
--   (<tt>threadDelay</tt> 10)</tt> this is the semantic equivalent to the
--   blocking <tt>readChan</tt> in the other implementations.
readChan :: IO () -> OutChan a -> IO a

-- | An <tt>IO</tt> action that returns a particular enqueued element when
--   and if it becomes available.
--   
--   Each <tt>Element</tt> corresponds to a particular enqueued element,
--   i.e. a returned <tt>Element</tt> always offers the only means to
--   access one particular enqueued item. The value returned by
--   <tt>tryRead</tt> moves monotonically from <tt>Nothing</tt> to <tt>Just
--   a</tt> when and if an element becomes available, and is idempotent at
--   that point.
newtype Element a
Element :: IO (Maybe a) -> Element a
[tryRead] :: Element a -> IO (Maybe a)

-- | An action that returns <tt>False</tt> sometime after the chan no
--   longer has any writers.
--   
--   After <tt>False</tt> is returned, any <a>tryRead</a> which returns
--   <tt>Nothing</tt> can be considered to be dead. Likewise for
--   <a>tryReadNext</a>. Note that in the blocking implementations a
--   <tt>BlockedIndefinitelyOnMVar</tt> exception is raised, so this
--   function is unnecessary.
isActive :: OutChan a -> IO Bool

-- | Write a value to the channel.
writeChan :: InChan a -> a -> IO ()

-- | Write an entire list of items to a chan type. Writes here from
--   multiple threads may be interleaved, and infinite lists are supported.
writeList2Chan :: InChan a -> [a] -> IO ()

-- | Duplicate a chan: the returned <tt>OutChan</tt> begins empty, but data
--   written to the argument <tt>InChan</tt> from then on will be available
--   from both the original <tt>OutChan</tt> and the one returned here,
--   creating a kind of broadcast channel.
--   
--   See also <a>streamChan</a> for a faster alternative that might be
--   appropriate.
dupChan :: InChan a -> IO (OutChan a)

-- | An infinite stream of elements. <a>tryReadNext</a> can be called any
--   number of times from multiple threads, and returns a value which moves
--   monotonically from <a>Pending</a> to <a>Next</a> if and when a head
--   element becomes available. <tt>isActive</tt> can be used to determine
--   if the stream has expired.
newtype Stream a
Stream :: IO (Next a) -> Stream a
[tryReadNext] :: Stream a -> IO (Next a)
data Next a

-- | The next head element along with the tail <tt>Stream</tt>.
Next :: a -> (Stream a) -> Next a

-- | The next element is not yet in the queue; you can retry
--   <a>tryReadNext</a> until a <tt>Next</tt> is returned.
Pending :: Next a

-- | Produce the specified number of interleaved "streams" from a chan.
--   Nextuming a <a>Stream</a> is much faster than calling
--   <a>tryReadChan</a>, and might be useful when an MPSC queue is needed,
--   or when multiple consumers should be load-balanced in a round-robin
--   fashion.
--   
--   Usage example:
--   
--   <pre>
--   do mapM_ (<a>writeChan</a> i) [1..9]
--      [str1, str2, str2] &lt;- <a>streamChan</a> 3 o
--      forkIO $ printStream str1   -- prints: 1,4,7
--      forkIO $ printStream str2   -- prints: 2,5,8
--      forkIO $ printStream str3   -- prints: 3,6,9
--    where 
--      printStream str = do
--        h &lt;- <a>tryReadNext</a> str
--        case h of
--          <a>Next</a> a str' -&gt; print a &gt;&gt; printStream str'
--          -- We know that all values were already written, so a Pending tells 
--          -- us we can exit; in other cases we might call <tt>yield</tt> and then 
--          -- retry that same <tt><a>tryReadNext</a> str</tt>:
--          <a>Pending</a> -&gt; return ()
--   </pre>
--   
--   Be aware: if one stream consumer falls behind another (e.g. because it
--   is slower) the number of elements in the queue which can't be GC'd
--   will grow. You may want to do some coordination of <a>Stream</a>
--   consumers to prevent this.
streamChan :: Int -> OutChan a -> IO [Stream a]

module Control.Concurrent.Chan.Unagi

-- | Create a new channel, returning its write and read ends.
newChan :: IO (InChan a, OutChan a)

-- | The write end of a channel created with <tt>newChan</tt>.
data InChan a

-- | The read end of a channel created with <tt>newChan</tt>.
data OutChan a

-- | Read an element from the chan, blocking if the chan is empty.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>readChan</tt> the message that the read would have returned is
--   likely to be lost, even when the read is known to be blocked on an
--   empty queue. If you need to handle this scenario, you can use
--   <a>readChanOnException</a>.
readChan :: OutChan a -> IO a

-- | Like <a>readChan</a> but allows recovery of the queue element which
--   would have been read, in the case that an async exception is raised
--   during the read. To be precise exceptions are raised, and the handler
--   run, only when <tt>readChanOnException</tt> is blocking.
--   
--   The second argument is a handler that takes a blocking IO action
--   returning the element, and performs some recovery action. When the
--   handler is called, the passed <tt>IO a</tt> is the only way to access
--   the element.
readChanOnException :: OutChan a -> (IO a -> IO ()) -> IO a

-- | Returns immediately with:
--   
--   <ul>
--   <li>an <tt><a>Element</a> a</tt> future, which returns one unique
--   element when it becomes available via <a>tryRead</a>.</li>
--   <li>a blocking <tt>IO</tt> action that returns the element when it
--   becomes available.</li>
--   </ul>
--   
--   If you're using this function exclusively you might find the
--   implementation in <a>Control.Concurrent.Chan.Unagi.NoBlocking</a> is
--   faster.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>tryReadChan</tt> the message that the read would have returned is
--   likely to be lost, just as it would be when raised directly after this
--   function returns.
tryReadChan :: OutChan a -> IO (Element a, IO a)

-- | An <tt>IO</tt> action that returns a particular enqueued element when
--   and if it becomes available.
--   
--   Each <tt>Element</tt> corresponds to a particular enqueued element,
--   i.e. a returned <tt>Element</tt> always offers the only means to
--   access one particular enqueued item. The value returned by
--   <tt>tryRead</tt> moves monotonically from <tt>Nothing</tt> to <tt>Just
--   a</tt> when and if an element becomes available, and is idempotent at
--   that point.
newtype Element a
Element :: IO (Maybe a) -> Element a
[tryRead] :: Element a -> IO (Maybe a)

-- | Return a lazy list representing the contents of the supplied OutChan,
--   much like System.IO.hGetContents.
getChanContents :: OutChan a -> IO [a]

-- | Write a value to the channel.
writeChan :: InChan a -> a -> IO ()

-- | Write an entire list of items to a chan type. Writes here from
--   multiple threads may be interleaved, and infinite lists are supported.
writeList2Chan :: InChan a -> [a] -> IO ()

-- | Duplicate a chan: the returned <tt>OutChan</tt> begins empty, but data
--   written to the argument <tt>InChan</tt> from then on will be available
--   from both the original <tt>OutChan</tt> and the one returned here,
--   creating a kind of broadcast channel.
dupChan :: InChan a -> IO (OutChan a)

module Control.Concurrent.Chan.Unagi.Bounded

-- | Create a new channel of the passed size, returning its write and read
--   ends.
--   
--   The passed integer bounds will be rounded up to the next highest power
--   of two, <tt>n</tt>. The queue may grow up to size <tt>2*n</tt> (see
--   <a>writeChan</a> for details), and the resulting chan pair requires
--   O(n) space.
newChan :: Int -> IO (InChan a, OutChan a)

-- | The write end of a channel created with <tt>newChan</tt>.
data InChan a

-- | The read end of a channel created with <tt>newChan</tt>.
data OutChan a

-- | Read an element from the chan, blocking if the chan is empty.
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>readChan</tt> the message that the read would have returned is
--   likely to be lost, even when the read is known to be blocked on an
--   empty queue. If you need to handle this scenario, you can use
--   <a>readChanOnException</a>.
readChan :: OutChan a -> IO a

-- | Like <a>readChan</a> but allows recovery of the queue element which
--   would have been read, in the case that an async exception is raised
--   during the read. To be precise exceptions are raised, and the handler
--   run, only when <tt>readChanOnException</tt> is blocking.
--   
--   The second argument is a handler that takes a blocking IO action
--   returning the element, and performs some recovery action. When the
--   handler is called, the passed <tt>IO a</tt> is the only way to access
--   the element.
readChanOnException :: OutChan a -> (IO a -> IO ()) -> IO a

-- | Returns immediately with:
--   
--   <ul>
--   <li>an <tt><a>Element</a> a</tt> future, which returns one unique
--   element when it becomes available via <a>tryRead</a>.</li>
--   <li>a blocking <tt>IO</tt> action that returns the element when it
--   becomes available.</li>
--   </ul>
--   
--   <i>Note re. exceptions</i>: When an async exception is raised during a
--   <tt>tryReadChan</tt> the message that the read would have returned is
--   likely to be lost, just as it would be when raised directly after this
--   function returns.
tryReadChan :: OutChan a -> IO (Element a, IO a)

-- | An <tt>IO</tt> action that returns a particular enqueued element when
--   and if it becomes available.
--   
--   Each <tt>Element</tt> corresponds to a particular enqueued element,
--   i.e. a returned <tt>Element</tt> always offers the only means to
--   access one particular enqueued item. The value returned by
--   <tt>tryRead</tt> moves monotonically from <tt>Nothing</tt> to <tt>Just
--   a</tt> when and if an element becomes available, and is idempotent at
--   that point.
newtype Element a
Element :: IO (Maybe a) -> Element a
[tryRead] :: Element a -> IO (Maybe a)

-- | Return a lazy list representing the contents of the supplied OutChan,
--   much like System.IO.hGetContents.
getChanContents :: OutChan a -> IO [a]

-- | Return the estimated length of a bounded queue
--   
--   The more concurrent writes and reads that are happening, the more
--   inaccurate the estimate of the chan's size is likely to be.
estimatedLength :: InChan a -> IO Int

-- | Write a value to the channel. If the chan is full this will block.
--   
--   To be precise this <i>may</i> block when the number of elements in the
--   queue <tt>&gt;= size</tt>, and will certainly block when <tt>&gt;=
--   size*2</tt>, where <tt>size</tt> is the argument passed to
--   <tt>newChan</tt>, rounded up to the next highest power of two.
--   
--   <i>Note re. exceptions</i>: In the case that an async exception is
--   raised while blocking here, the write will nonetheless succeed. When
--   not blocking, exceptions are masked. Thus writes always succeed once
--   <a>writeChan</a> is entered.
writeChan :: InChan a -> a -> IO ()

-- | Try to write a value to the channel, aborting if the write is likely
--   to exceed the bounds, returning a <tt>Bool</tt> indicating whether the
--   write was successful.
--   
--   This function never blocks, but may occasionally write successfully to
--   a queue that is already "full". Unlike <a>writeChan</a> this function
--   treats the requested bounds (raised to nearest power of two) strictly,
--   rather than using the <tt>n .. n*2</tt> range. The more concurrent
--   writes and reads that are happening, the more inaccurate the estimate
--   of the chan's size is likely to be.
tryWriteChan :: InChan a -> a -> IO Bool

-- | Write an entire list of items to a chan type. Writes here from
--   multiple threads may be interleaved, and infinite lists are supported.
writeList2Chan :: InChan a -> [a] -> IO ()

-- | Duplicate a chan: the returned <tt>OutChan</tt> begins empty, but data
--   written to the argument <tt>InChan</tt> from then on will be available
--   from both the original <tt>OutChan</tt> and the one returned here,
--   creating a kind of broadcast channel.
--   
--   Writers will be blocked only when the fastest reader falls behind the
--   bounds; slower readers of duplicated <a>OutChan</a> may fall
--   arbitrarily behind.
dupChan :: InChan a -> IO (OutChan a)
