General advice for a change. This is an implementation of message queues that I'm going to use for some work on an actors model library.
(defclass message-queue () ((messages :accessor messages :initarg :messages :initform nil) (last-cons :accessor last-cons :initarg :last-cons :initform nil :documentation "Cached end of the list") (len :accessor len :initarg :len :initform 0 :documentation "Cached message queue length. Modified by enqueue and dequeue") (lock :initform (bt:make-lock) :accessor lock :documentation "Lock for this message queue") (max-len :accessor max-len :initarg :max-len :initform nil :documentation "If present, queue maintains at most this many elements") (flag :initform (bt:make-condition-variable) :accessor flag :documentation "Condition variable used to notify that a message was enqueued"))) (defun make-queue (&optional max-len) (make-instance 'message-queue :max-len max-len)) (defmethod full-p ((queue sized-queue)) (with-slots (len max-len) (and max-len (>= len max-len)))) (defmethod empty-p ((queue message-queue)) (= (len queue) 0)) (defmethod enqueue (object (queue message-queue)) "Adds an element to the back of the given queue in a thread-safe way." (with-slots (lock messages max-len len flag last-cons) queue (with-lock-held (lock) (let ((o (list object))) (cond ((empty-p queue) (setf messages o last-cons messages len 1)) ((full-p queue) (pop messages) (setf (cdr last-cons) o last-cons o)) (t (setf (cdr last-cons) o last-cons o) (incf len))))) (condition-notify flag) messages)) (defmethod dequeue ((queue message-queue) &optional (timeout 0)) "Pops a message from the given queue in a thread-safe way. If the target queue is empty, blocks until a message arrives. If timeout is not zero, errors after timeout." (with-slots (messages lock flag len) queue (with-timeout (timeout) (with-lock-held (lock) (unless messages (condition-wait flag lock)) (decf len) (pop messages))))) (defmethod dequeue-no-hang ((queue message-queue)) "Pops a message from the given queue in a thread-safe way. If the target queue is empty, returns NIL. The second value specifies whether an item was found in queue (this is meant to disambiguate the situation where a queue contains the message NIL)" (with-slots (messages lock flag len) queue (with-lock-held (lock) (if messages (progn (decf len) (values (pop messages) t)) (values nil nil)))))