1.0.37.57: better DEFMETHOD pretty-printing
[sbcl/pkhuong.git] / contrib / sb-concurrency / queue.lisp
blobfec6d0fe818d2e073b2c2842edb64d9b576a0fb1
1 ;;;; Lock-free FIFO queues, from "An Optimistic Approach to Lock-Free FIFO
2 ;;;; Queues" by Edya Ladan-Mozes and Nir Shavit.
3 ;;;;
4 ;;;; Written by Nikodemus Siivola for SBCL.
5 ;;;;
6 ;;;; This software is part of the SBCL system. See the README file for
7 ;;;; more information.
8 ;;;;
9 ;;;; This software is derived from the CMU CL system, which was written at
10 ;;;; Carnegie Mellon University and released into the public domain. The
11 ;;;; software is in the public domain and is provided with absolutely no
12 ;;;; warranty. See the COPYING and CREDITS files for more information.
14 (in-package :sb-concurrency)
16 (defconstant +dummy+ '.dummy.)
18 (declaim (inline make-node))
19 (defstruct node
20 value
21 (prev nil :type (or null node))
22 (next nil :type (or null node)))
24 (declaim (inline %make-queue))
25 (defstruct (queue (:constructor %make-queue (head tail name))
26 (:copier nil)
27 (:predicate queuep))
28 "Lock-free thread safe queue."
29 (head (error "No HEAD.") :type node)
30 (tail (error "No TAIL.") :type node)
31 (name nil))
33 (setf (documentation 'queuep 'function)
34 "Returns true if argument is a QUEUE, NIL otherwise."
35 (documentation 'queue-name 'function)
36 "Name of a QUEUE. Can be assingned to using SETF. Queue names
37 can be arbitrary printable objects, and need not be unique.")
39 (defun make-queue (&key name initial-contents)
40 "Returns a new QUEUE with NAME and contents of the INITIAL-CONTENTS
41 sequence enqueued."
42 (let* ((dummy (make-node :value +dummy+))
43 (queue (%make-queue dummy dummy name)))
44 (flet ((enc-1 (x)
45 (enqueue x queue)))
46 (declare (dynamic-extent #'enc-1))
47 (map nil #'enc-1 initial-contents))
48 queue))
50 (defun enqueue (value queue)
51 "Adds VALUE to the end of QUEUE. Returns VALUE."
52 (let ((node (make-node :value value)))
53 (loop for tail = (queue-tail queue)
54 do (setf (node-next node) tail)
55 (when (eq tail (sb-ext:compare-and-swap (queue-tail queue) tail node))
56 (setf (node-prev tail) node)
57 (return value)))))
59 (defun dequeue (queue)
60 "Retrieves the oldest value in QUEUE and returns it as the primary value,
61 and T as secondary value. If the queue is empty, returns NIL as both primary
62 and secondary value."
63 (tagbody
64 :continue
65 (let* ((head (queue-head queue))
66 (tail (queue-tail queue))
67 (first-node-prev (node-prev head))
68 (val (node-value head)))
69 (when (eq head (queue-head queue))
70 (cond ((not (eq val +dummy+))
71 (if (eq tail head)
72 (let ((dummy (make-node :value +dummy+ :next tail)))
73 (when (eq tail (sb-ext:compare-and-swap (queue-tail queue)
74 tail dummy))
75 (setf (node-prev head) dummy))
76 (go :continue))
77 (when (null first-node-prev)
78 (fixList queue tail head)
79 (go :continue)))
80 (when (eq head (sb-ext:compare-and-swap (queue-head queue)
81 head first-node-prev))
82 ;; This assignment is not present in the paper, but is
83 ;; equivalent to the free(head.ptr) call there: it unlinks
84 ;; the HEAD from the queue -- the code in the paper leaves
85 ;; the dangling pointer in place.
86 (setf (node-next first-node-prev) nil)
87 (return-from dequeue (values val t))))
88 ((eq tail head)
89 (return-from dequeue (values nil nil)))
90 ((null first-node-prev)
91 (fixList queue tail head)
92 (go :continue))
94 (sb-ext:compare-and-swap (queue-head queue)
95 head first-node-prev)))))
96 (go :continue)))
98 (defun fixlist (queue tail head)
99 (let ((current tail))
100 (loop while (and (eq head (queue-head queue)) (not (eq current head)))
101 do (let ((next (node-next current)))
102 (when (not next)
103 (return-from fixlist nil))
104 (let ((nextNodePrev (node-prev next)))
105 (when (not (eq nextNodePrev current))
106 (setf (node-prev next) current))
107 (setf current next))))))
109 (defun list-queue-contents (queue)
110 "Returns the contents of QUEUE as a list without removing them from the
111 QUEUE. Mainly useful for manual examination of queue state."
112 (let (all)
113 (labels ((walk (node)
114 ;; Since NEXT pointers are always right, traversing from tail
115 ;; to head is safe.
116 (let ((value (node-value node))
117 (next (node-next node)))
118 (unless (eq +dummy+ value)
119 (push value all))
120 (when next
121 (walk next)))))
122 (walk (queue-tail queue)))
123 all))
125 (defun queue-count (queue)
126 "Returns the number of objects in QUEUE. Mainly useful for manual
127 examination of queue state, and in PRINT-OBJECT methods: inefficient as it
128 walks the entire queue."
129 (let ((n 0))
130 (declare (unsigned-byte n))
131 (labels ((walk (node)
132 (let ((value (node-value node))
133 (next (node-next node)))
134 (unless (eq +dummy+ value)
135 (incf n))
136 (when next
137 (walk next)))))
138 (walk (queue-tail queue))
139 n)))
141 (defun queue-empty-p (queue)
142 "Returns T if QUEUE is empty, NIL otherwise."
143 (let* ((head (queue-head queue))
144 (tail (queue-tail queue))
145 (val (node-value head)))
146 (and (eq head tail) (eq val +dummy+))))