https://github.com/rpav/trivial-channels.git
git clone 'https://github.com/rpav/trivial-channels.git'
(ql:quickload :trivial-channels)
This is a very, very trivial implementation of channels (and a queue). I find myself using it in a few places where very trivial message passing is needed and a more complex, robust solution would be overkill.
(let ((channel (make-channel)))
(sendmsg channel 'anything)
(recvmsg channel)) ;; => ANYTHING
(make-channel): Make a channel(sendmsg CHANNEL OBJECT): Send OBJECT on CHANNEL(recvmsg CHANNEL &optional TIMEOUT): Receive on CHANNEL,
optionally timing out after TIMEOUT seconds(getmsg CHANNEL): Get a message if available, or NIL(hasmsg CHANNEL): Whether the channel has a message. No guarantee
it will still have one after the call, if there are multiple
receivers sharing a channel.Notably, recvmsg supports a timeout. These functions properly lock
and it's safe to share a channel between threads (that being the
entire purpose).
While trivial-channels should be simple enough you can adapt it to many usage patterns, for simple bi-directional message passing I have found it easiest to simply pass a message with a return-channel included:
;;; Sender:
(defvar *global-listener* (make-channel))
(defvar *done* nil)
(let* ((return-channel (make-channel))
(msg (cons 'value return-channel)))
(sendmsg *global-listener* msg)
(recvmsg return-channel))
;;; Meanwhile, in another thread:
(loop until *done* do
(let ((msg (recvmsg *global-listener*)))
(let ((value (car msg))
(channel (cdr msg)))
;; insert useful things here
(sendmsg channel ...))))
Of course, you needn't create a new return channel every time, either, if you are worried about consing, but this is an easy way to pass functions to a specific thread, implement actors, etc.
The queue used to implement this is also available via the package
:trivial-channels.queue, since it's sometimes handy to have a
trivial queue, too.
Queues do not lock.
Queues are implemented with conses.
(make-queue): Make a queue(queue-head Q): Head of the queue(queue-tail Q): Tail of the queue(queue-add-cons Q CONS): CONS becomes the tail of the queue; its
CDR may be destructively modified(queue-add Q ITEM): Add ITEM to the tail of the queue(queue-push Q ITEM): Push ITEM onto the front of the queue(queue-pop Q): Pop and return and item from the queue, or NIL(queue-pop-cons Q): Pop and return the cons from the queue, or NIL(queue-has-item-p Q): If there are items in the queue, or NIL(queue-peek Q): The front item in the queue, or NIL(queue-pop-to Q1 Q2): Pop an item from Q1 and add it to Q2
without consing(queue-prepend-to Q1 Q2): Remove all items from Q1 and prepend
them to Q2 without consing; no value returnedAll add or remove type operations return the item (or cons) being handled, unless otherwise noted.