The author welcomes feedback, questions, help requests, and bug reports via e-mail: J.P. Larocque <jpl-software at thoughtcrime.us>

THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.

Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software.

That software contains parts derived from an earlier library by Rob Pike, Sape Mullender, and Russ Cox:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

That software was derived from channel.c of Plan 9 libthread:

This software was directly derived from csp.tgz , dated 2006-07-03, and published by Roger Peppe. No copyright notice giving attribution to Roger Peppe or any specific licensing terms seem to have been included in that version.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

The software and this document are licensed under permissive, BSD-like terms, copied from the ISC license:

I sign all my software with OpenPGP, key ID 0xE979FFBEA002D20F, fingerprint A87B 1C5A 28C4 03BD 54BA CE8E E979 FFBE A002 D20F. (Releases were previously signed with 0x80555CED7394F948, which has been revoked, but not compromised. See my OpenPGP transition statement .)

The latest version of Calispel, with accompanying documentation, can be found at: http://www.thoughtcrime.us/software/calispel/

Many CSP- and occam-style channel libraries offer features like parallel execution (i.e. occam PAR ). Calispel is a message-passing library, and as such leaves the role of threading abstractions and utilities left to be filled by perfectly good, complementary libraries such as Bordeaux-Threads and Eager Future .

A syntax for alternation is provided (like ALT in occam, or Unix select() ): given a sequence of operations, any or all of which may block, alternation selects the first operation that doesn't block and executes associated code. Alternation can also time out, executing an "otherwise" clause if no operation becomes available within a set amount of time.

Because sending and receiving on a channel may block, either operation can time out after a specified amount of time.

Calispel channels let one thread communicate with another, facilitating unidirectional communication of any Lisp object. Channels may be unbuffered, where a sender waits for a receiver (or vice versa) before either operation can continue, or channels may be buffered with flexible policy options.

Calispel is a Common Lisp library for thread-safe message-passing channels, in the style of the occam programming language.

2. Examples

Create a channel with no buffering:

(defparameter *chan* (make-instance 'calispel:channel))

In another thread, sleep for 1 second, then send the number 42 to the channel. In the current thread, receive from the channel. At first, there will be no value available, so ? will wait until the other thread sends the value.

(progn (eager-future:pexec (sleep 1) (calispel:! *chan* 42)) (calispel:? *chan*)) => 42 T

( 42 is the value received, and T indicates that the receive was successful—it did not time out.)

Sending to the channel will also block without a waiting receiver, because channels are unbuffered by default. This will attempt to send to the channel, then time out after 2 seconds:

(calispel:! *chan* 'foo 2) => NIL

( NIL indicates that the send was not successful—it timed out.)

Create a new channel that is buffered:

This channel will accept up to two values that have not yet been received before sends will block:

(loop for i from 1 while (calispel:! *chan* i 0) finally (format t "~&Stopped before ~:R value.~&" i)) >> Stopped before third value.

Now let's print them back out:

(loop (multiple-value-bind (value success?) (calispel:? *chan* 0) (when success? (format t "~&Value: ~S~&" value)) (unless success? (return)))) >> Value: 1 Value: 2

Suppose that we have many channels that we're interested in receiving from or sending to. We can use alternation to select the first operation that is available, and then perform some action associated with the operation:

(let ((chan1 (make-instance 'calispel:channel)) ; chan1 goes unused (chan2 (make-instance 'calispel:channel))) (eager-future:pexec (calispel:! chan2 42)) (calispel:pri-alt ((calispel:? chan1) ;; Nothing is sent to CHAN1, so it can't be ready. (format t "~&Got a value from CHAN1, but that should never happen.~&")) ((calispel:? chan2 value) ;; CHAN2 has either had something sent to it, or it soon will, ;; so this will execute. (format t "~&Got value from CHAN2: ~S~&" value)))) >> Got value from CHAN2: 42

What if there's more than one operation that is immediately possible? PRI-ALT chooses the first one available...

(let ((chan1 (make-instance 'calispel:channel)) (chan2 (make-instance 'calispel:channel))) (eager-future:pexec (calispel:! chan1 'foo)) (eager-future:pexec (calispel:! chan2 'bar)) (sleep 1) ; Wait for both CHAN1 and CHAN2 to become ready. (calispel:pri-alt ((calispel:? chan1 value) (format t "~&Got value from CHAN1: ~S~&" value)) ((calispel:? chan2 value) (format t "~&Got value from CHAN2: ~S~&" value)))) >> Got value from CHAN1: FOO

...whereas FAIR-ALT chooses any of the available operations:

(let ((chan1 (make-instance 'calispel:channel)) (chan2 (make-instance 'calispel:channel))) (eager-future:pexec (calispel:! chan1 'foo)) (eager-future:pexec (calispel:! chan2 'bar)) (sleep 1) ; Wait for both CHAN1 and CHAN2 to become ready. (calispel:fair-alt ((calispel:? chan1 value) (format t "~&Got value from CHAN1: ~S~&" value)) ((calispel:? chan2 value) (format t "~&Got value from CHAN2: ~S~&" value)))) >> Got value from CHAN1: FOO (or, determined randomly) >> Got value from CHAN2: BAR

Just like ? and ! , PRI-ALT and FAIR-ALT allow time outs to be specified. An OTHERWISE clause is executed if no operation can be immediately performed, effectively putting a time out of 0 on all the operations:

(let ((chan1 (make-instance 'calispel:channel)) (chan2 (make-instance 'calispel:channel))) (eager-future:pexec (sleep 1) (calispel:! chan1 'foo)) (calispel:pri-alt ((calispel:? chan1 value) (format t "~&Got value from CHAN1: ~S~&" value)) ((calispel:? chan2 value) (format t "~&Got value from CHAN2: ~S~&" value)) (otherwise (format t "~&Timed-out.~&")))) >> Timed-out.

You can also wait up to a certain amount of time before executing the OTHERWISE clause:

(let ((chan1 (make-instance 'calispel:channel)) (chan2 (make-instance 'calispel:channel))) (eager-future:pexec (sleep 1) (calispel:! chan1 'foo)) (calispel:pri-alt ((calispel:? chan1 value) (format t "~&Got value from CHAN1: ~S~&" value)) ((calispel:? chan2 value) (format t "~&Got value from CHAN2: ~S~&" value)) ((otherwise :timeout 5) (format t "~&Timed-out.~&")))) >> Got value from CHAN1: FOO