synchronous-channels-0.1: Synchronous communication channels

Portabilitysomewhat portable?




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.


The channel datatype

data Chan a Source

The abstract channel type for sending values of type a.

Construction and observation

newChan :: IO (Chan a)Source

Make a new channel.

isEmptyChan :: Chan a -> IO BoolSource

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 c
     writeIORef 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 aSource

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 aSource

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

data TryResult a Source

The synchronous, non-blocking operations may succeed immediately, or they may give up for a variety of reasons:



The operation succeeded.


getSuccess :: a

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.


Eq a => Eq (TryResult a) 
Show a => Show (TryResult a) 

maybeTry :: IO (TryResult a) -> IO (Maybe a)Source

Lift results of the try* operations into Maybe. Success goes to Just and all kinds of failure go to Nothing.

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.)