Copyright | (c) 2009 Jesse A. Tov |
---|---|
License | BSD (see the file LICENSE) |
Maintainer | tov@ccs.neu.edu |
Stability | experimental |
Portability | somewhat portable? |
Safe Haskell | None |
Language | Haskell98 |
This module provides synchronous channels. Unlike the channels in
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 immediately.
- Asynchronous
- 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
- = Success {
- getSuccess :: a
- | NotReady
- | TryAgain
- | WouldBlock
- = Success {
- 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
isEmptyChan :: Chan a -> IO Bool Source
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 <-readChan
cwriteIORef
r 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
operations.
Basic operations
writeChan :: Chan a -> a -> IO () Source
Write a value to a channel, possibly blocking until synchronizing with a reader.
readChan :: Chan a -> IO a Source
Reads a value from a channel, potentially blocking until a writer is ready to synchronize.
Questionable operations
unGetChan :: Chan a -> a -> IO () Source
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.
swapChan :: Chan a -> a -> IO a Source
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.
CAUTION: This operation does not guarantee that the read and
subsequent write are atomic. It is somewhat likely to be better
in that respect than readChan
followed by unGetChan
, however.
List operations
getChanContents :: Chan a -> IO [a] Source
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.
getChanN :: Chan a -> Integer -> IO [a] Source
Read a given number of elements from the channel as a lazy list.
Like 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.)
writeList2Chan :: Chan a -> [a] -> IO () Source
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
whole list, 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
TryAgain
.
The non-blocking result datatype
The synchronous, non-blocking operations may succeed immediately, or they may give up for a variety of reasons:
Success | The operation succeeded. |
| |
NotReady | No other thread is currently ready to synchronize for the requested operation. |
TryAgain | 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. |
WouldBlock | Another thread is currently operating on the channel. It may be worth trying again very soon. |
Basic operations
tryWriteChan :: Chan a -> a -> IO (TryResult ()) Source
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.
tryReadChan :: Chan a -> IO (TryResult a) Source
Attempts to read a value from a channel, succeeding immediately if a writer is already available to synchronize.
tryPeekChan :: Chan a -> IO (TryResult a) Source
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.
List operations
tryGetChanContents :: Chan a -> IO (TryResult [a]) Source
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 writeList2Chan
).
tryGetChanN :: Chan a -> Integer -> IO (TryResult [a]) Source
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.
tryWriteList2Chan :: Chan a -> [a] -> IO (TryResult (), [a]) Source
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).
Asynchronous operations
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).
asyncWriteChan :: Chan a -> a -> IO () Source
Write a value to a channel, returning immediately rather than waiting for the reader to arrive.
asyncUnGetChan :: Chan a -> a -> IO () Source
Write a value to the "read end" of a channel, succeeding immediately rather than waiting for a reader.
tryAsyncSwapChan :: Chan a -> a -> IO (TryResult a) Source
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.
Unlike swapChan
, guarantees that no other write can intervene.
asyncWriteList2Chan :: Chan a -> [a] -> IO () Source
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.)