This module provides synchronous channels. Unlike the channels in
Control.Concurrent.Chan, which are unbounded queues on which
writers never block, these channels allow each writer to block until
it synchronizes with the particular reader that receives its message.
We actually provide three classes of channel operations:
- Synchronous, blocking
- These operations block until they synchronize their communication with another thread.
- Synchronous, non-blocking
- These operations complete immediately if another thread is ready to synchronize, and otherwise return a failure code immediate.
- These operations complete immediately and always succeed, though the value they send may not be received until another thread tries to receive it.
- data Chan a
- newChan :: IO (Chan a)
- isEmptyChan :: Chan a -> IO Bool
- writeChan :: Chan a -> a -> IO ()
- readChan :: Chan a -> IO a
- unGetChan :: Chan a -> a -> IO ()
- swapChan :: Chan a -> a -> IO a
- getChanContents :: Chan a -> IO [a]
- getChanN :: Chan a -> Integer -> IO [a]
- writeList2Chan :: Chan a -> [a] -> IO ()
- data TryResult a
- maybeTry :: IO (TryResult a) -> IO (Maybe a)
- tryWriteChan :: Chan a -> a -> IO (TryResult ())
- tryReadChan :: Chan a -> IO (TryResult a)
- tryPeekChan :: Chan a -> IO (TryResult a)
- tryGetChanContents :: Chan a -> IO (TryResult [a])
- tryGetChanN :: Chan a -> Integer -> IO (TryResult [a])
- tryWriteList2Chan :: Chan a -> [a] -> IO (TryResult (), [a])
- asyncWriteChan :: Chan a -> a -> IO ()
- asyncUnGetChan :: Chan a -> a -> IO ()
- tryAsyncSwapChan :: Chan a -> a -> IO (TryResult a)
- asyncWriteList2Chan :: Chan a -> [a] -> IO ()
The channel datatype
Construction and observation
Is the channel currently empty? Note that the answer may become false arbitrarily soon. Don't rely on this operation.
Synchronous, blocking operations
The synchronous, blocking channel operations are designed to complete only when a writing thread synchronizes with a reading thread.
They are exception safe, in the sense that if an asynchronous exception is delivered to a blocked thread, that thread is removed from the pool and cannot synchronize with another. In particular, we can write code like this:
block$ do msg <-
In this case, the call to
readChan may be interrupted, but
if the message is delivered, the
writeIORef will happen. There
is no case where the writing thread synchronizes and unblocks
and the message is dropped on the floor. This make it possible
to safely implement features such as timeouts on blocking
Write a value to a channel, possibly blocking until synchronizing with a reader.
Reads a value from a channel, potentially blocking until a writer is ready to synchronize.
Write to the "read end" of a channel. If several writers are waiting, this jumps ahead of them to the front of the line. Blocks until matched up with a reader.
Reads a value from a channel, replacing it with a different value. Blocks until the replacement value is read, and then returns the old value.
Read the contents of the channel as a lazy list. While this operation returns immediately, forcing evaluation of the list will block, which is why this is included among the blocking operations. Writers will block until each link in the list is forced as well.
Any subsequent attempts to read from the channel will fail, unless a thread is interrupted while blocking on forcing the list. Don't rely on this behavior.
Read a given number of elements from the channel as a lazy list.
getChanContents, this operation returns immediately, but it
will block when the list is forced. (Taking the length of the list
should block until all the matching writes complete.)
Write a list to a channel, blocking until the read completes.
It is guaranteed that no other writes can intervene among the
list elements. (This cannot be implemented in terms of
writeChan.) The list may be infinite, in which case this
operation never completes.
Interrupting this operation before the list is read completely causes
the rest of the list not to be written. (If you want to write the
asyncWriteList2Chan may be suitable.)
Synchronous, non-blocking operations
These operations are similar to the blocking operations in that they only succeed by synchronizing with another thread, but they return a failure code rather than block if no other thread is ready or if they cannot acquire a lock on the channel.
Generally, a non-blocking operation from this section cannot synchronize with another non-blocking operation. The other operation that pairs up with one of these operations will always be blocking or asynchronous.
These operations are designed to operate in constant time
(or linear time for the list operations).
In particular, it may be possible to attempt to synchronize with
a waiting thread that gives up before the operation is complete.
Rather than look for another opportunity, which could lead to
an arbitrary number of attempts, the operation fails with
The non-blocking result datatype
The synchronous, non-blocking operations may succeed immediately, or they may give up for a variety of reasons:
The operation succeeded.
No other thread is currently ready to synchronize for the requested operation.
An attempt was made to synchronize with another thread, but the other thread bailed out before it could complete. Another thread may be available, so it may be worth trying again immediately.
Another thread is currently operating on the channel. It may be worth trying again very soon.
Attempts to write a value to a channel, succeeding immediately if a reader is already available to synchronize. Will fail if the reader is interrupted before the operation completes, if there is no reader available, or if another thread is currently starting an operation on the channel.
Attempts to read a value from a channel, succeeding immediately if a writer is already available to synchronize.
Attempts to read a value from a channel, but does not allow a writer to synchronize, and does not remove the observed value from the channel. Fails if no writer is currently available, if the first writer has bailed, or if it cannot immediately get a lock on the channel.
Read the currently available elements from the channel as a lazy
list. The list is lazy because the number of currently available
elements may be infinite (see
Read up to the given number of currently available elements from the channel. The list will be no longer than the given number, but if there are insufficient writers available then it may be shorter. The writers will block until their portions of the list's spine are forced.
Attempt to write as much of a list as possible to a channel synchronously, but without blocking; returns the unwritten remainder of the list. This operation will write additional list elements so long as -- there are readers ready to receive them (and so long as the list doesn't run out).
The asynchronous operations always succeed immediately. They should be semantically equivalent to forking another thread and performing the equivalent blocking operation (though they do not actually require a separate thread).
Write a value to a channel, returning immediately rather than waiting for the reader to arrive.
Write a value to the "read end" of a channel, succeeding immediately rather than waiting for a reader.
If a writer is available to synchronize with, synchronizes with the
writer, allowing its operation to complete, writes the replacement
value ahead of any other writers, and then returns immediately.
swapChan, guarantees that no other write can intervene.
Write a list to a channel, succeeding immediately. The list may
be infinite, in which case the operation still completes
immediately. (Actually, it may take time proportional to the number
of readers that are ready, so if an infinite list is written to
getChanContents on the other side, it may not actually complete.)