1 (uiop:define-package
#:lw2.rwlock
3 (:import-from
#:sb-thread
4 #:make-mutex
#:with-mutex
#:grab-mutex
#:release-mutex
5 #:make-waitqueue
#:condition-notify
#:condition-broadcast
#:condition-wait
)
7 #:atomic-incf
#:atomic-decf
)
9 #:without-interrupts
#:allow-with-interrupts
#:with-interrupts
)
10 (:import-from
#:alexandria
12 (:export
#:rwlock
#:make-rwlock
#:read-lock
#:read-unlock
#:write-lock
#:write-unlock
#:with-read-lock
#:with-write-lock
))
14 (in-package #:lw2.rwlock
)
16 (declaim (inline make-rwlock rwlock-readers rwlock-draining-readers
))
19 (readers 0 :type sb-ext
:word
)
20 (draining-readers 0 :type
(signed-byte 64))
21 (write-mutex (make-mutex))
22 (read-waitqueue-mutex (make-mutex))
23 (read-waitqueue (make-waitqueue))
24 (write-waitqueue-mutex (make-mutex))
25 (write-waitqueue (make-waitqueue)))
27 (defmacro with-rwlock-accessors
((rwlock) &body body
)
28 `(with-accessors ,(loop for var in
'(readers draining-readers write-mutex read-waitqueue-mutex read-waitqueue write-waitqueue-mutex write-waitqueue
)
29 collect
`(,var
,(find-symbol (format nil
"~A-~A" 'rwlock var
) '#:lw2.rwlock
)))
37 (declaim (inline read-lock read-unlock
))
39 (defun read-lock-slowpath (rwlock)
40 (with-rwlock-accessors (rwlock)
41 (with-mutex (read-waitqueue-mutex)
42 (loop until
(evenp (atomic-incf readers
0))
43 do
(or (condition-wait read-waitqueue read-waitqueue-mutex
) (error "Waitqueue error"))))
46 (defun read-lock (rwlock)
47 (with-rwlock-accessors (rwlock)
48 (let ((orig-readers (atomic-incf readers
2)))
49 (when (oddp orig-readers
)
50 (read-lock-slowpath rwlock
)))
53 (defun read-unlock-slowpath (rwlock)
54 (with-rwlock-accessors (rwlock)
55 (with-mutex (write-waitqueue-mutex)
56 (decf (the (signed-byte 61) draining-readers
))
57 (when (= draining-readers
0)
58 (condition-notify write-waitqueue
)))
61 (defun read-unlock (rwlock)
62 (with-rwlock-accessors (rwlock)
63 (let ((orig-readers (atomic-decf readers
2)))
64 (when (oddp orig-readers
)
65 (read-unlock-slowpath rwlock
)))
68 (defun write-lock (rwlock)
69 (with-rwlock-accessors (rwlock)
70 (grab-mutex write-mutex
)
71 (let ((orig-readers (atomic-incf readers
1)))
72 (unless (= orig-readers
0)
73 (with-mutex (write-waitqueue-mutex)
74 (incf (the (signed-byte 61) draining-readers
) (the (signed-byte 61) (ash orig-readers -
1)))
75 (loop until
(= draining-readers
0)
76 do
(or (condition-wait write-waitqueue write-waitqueue-mutex
) (error "Waitqueue error"))))))
79 (defun write-unlock (rwlock)
80 (with-rwlock-accessors (rwlock)
81 (with-mutex (read-waitqueue-mutex)
82 (atomic-decf readers
1)
83 (condition-broadcast read-waitqueue
))
84 (release-mutex write-mutex
)
87 (defmacro with-rwlock
((rwlock disposition
) &body body
)
88 (multiple-value-bind (lock unlock
) (ecase disposition
89 (:read
(values 'read-lock
'read-unlock
))
90 (:write
(values 'write-lock
'write-unlock
)))
92 (allow-with-interrupts
95 (with-interrupts ,@body
)
96 (,unlock
,rwlock
))))))
98 (defmacro with-read-lock
((rwlock &key upgrade-fn
) &body body
)
100 (with-gensyms (upgraded)
101 `(let ((,upgraded nil
))
102 (flet ((,upgrade-fn
()
104 (allow-with-interrupts
105 (read-unlock ,rwlock
)
107 (setf ,upgraded t
)))))
110 (with-interrupts ,@body
)
112 (read-unlock ,rwlock
)
113 (write-unlock ,rwlock
))))))
114 `(with-rwlock (,rwlock
:read
) ,@body
)))
116 (defmacro with-write-lock
((rwlock) &body body
)
117 `(with-rwlock (,rwlock
:write
) ,@body
))