- (tagbody
- :continue
- (let* ((head (queue-head queue))
- (tail (queue-tail queue))
- (first-node-prev (node-prev head))
- (val (node-value head)))
- (barrier (:read))
- (when (eq head (queue-head queue))
- (cond ((not (eq val +dummy+))
- (if (eq tail head)
- (let ((dummy (make-node :value +dummy+ :next tail)))
- (when (eq tail (sb-ext:compare-and-swap (queue-tail queue)
- tail dummy))
- (setf (node-prev head) dummy))
- (go :continue))
- (when (null first-node-prev)
- (fixList queue tail head)
- (go :continue)))
- (when (eq head (sb-ext:compare-and-swap (queue-head queue)
- head first-node-prev))
- ;; These assignment is not present in the paper, but are
- ;; equivalent to the free(head.ptr) call there.
- ;;
- ;; First we unlink the HEAD from the queue -- the code in
- ;; the paper leaves the dangling pointer in place.
- ;;
- ;; Then we NIL out the slots in HEAD to help the GC,
- ;; otherwise conservativism might lead to massive chains of
- ;; nodes being retained.
- (setf (node-next first-node-prev) nil
- (node-prev head) nil
- (node-next head) nil
- (node-value head) nil)
- (return-from dequeue (values val t))))
- ((eq tail head)
- (return-from dequeue (values nil nil)))
- ((null first-node-prev)
- (fixList queue tail head)
- (go :continue))
- (t
- (sb-ext:compare-and-swap (queue-head queue)
- head first-node-prev)))))
- (go :continue)))
+ ;; Attempt to CAS QUEUE-HEAD with the next node, repeat upon
+ ;; failure. Upon success, clear the discarded node and set the CAR
+ ;; of QUEUE-HEAD to +DUMMY+.
+ (declare (optimize speed))
+ (loop (let* ((head (queue-head queue))
+ (next (cdr head)))
+ ;; NEXT could be +DEAD-END+, whereupon we try again.
+ (typecase next
+ (null (return (values nil nil)))
+ (cons (when (eq head (sb-ext:compare-and-swap (queue-head queue)
+ head next))
+ (let ((value (car next)))
+ ;; Clear the CDR, otherwise the conservative GC could
+ ;; hoard long lists. (car head) is always +dummy+.
+ (setf (cdr head) +dead-end+
+ (car next) +dummy+)
+ (return (values value t)))))))))