(multiple-value-bind (vars vals old new cas-form read-form)
(sb!ext:get-cas-expansion place env)
`(let* (,@(mapcar #'list vars vals)
- (,owner ,read-form)
+ (,owner (progn
+ (barrier (:read))
+ ,read-form))
(,self *current-thread*)
(,old nil)
(,new ,self))
(unwind-protect
(progn
(unless (eq ,owner ,self)
- (loop while (setf ,owner (or ,read-form ,cas-form))
+ (loop until (loop repeat 100
+ when (and (progn
+ (barrier (:read))
+ (not ,read-form))
+ (not (setf ,owner ,cas-form)))
+ return t
+ else
+ do (sb!ext:spin-loop-hint))
do (thread-yield)))
,@body)
+ ;; FIXME: SETF + write barrier should to be enough here.
+ ;; ...but GET-CAS-EXPANSION doesn't return a WRITE-FORM.
+ ;; ...maybe it should?
(unless (eq ,owner ,self)
(let ((,old ,self)
(,new nil))
- ,cas-form)))))))
+ (unless (eq ,old ,cas-form)
+ (bug "Failed to release CAS lock!")))))))))
;;; Conditions
(multiple-value-list
(join-thread thread :default cookie))))
(state (if (eq :running info)
- (let* ((thing (thread-waiting-for thread)))
+ (let* ((thing (progn
+ (barrier (:read))
+ (thread-waiting-for thread))))
(typecase thing
(cons
(list "waiting on:" (cdr thing)
(unwind-protect
(progn
(setf (thread-waiting-for ,n-thread) ,new)
+ (barrier (:write))
,@forms)
;; Interrupt handlers and GC save and restore any
;; previous wait marks using WITHOUT-DEADLOCKS below.
- (setf (thread-waiting-for ,n-thread) nil)))))
+ (setf (thread-waiting-for ,n-thread) nil)
+ (barrier (:write))))))
\f
;;;; Mutexes
;;; depends on the current thread. Does not detect deadlocks from sempahores.
(defun check-deadlock ()
(let* ((self *current-thread*)
- (origin (thread-waiting-for self)))
+ (origin (progn
+ (barrier (:read))
+ (thread-waiting-for self))))
(labels ((detect-deadlock (lock)
(let ((other-thread (mutex-%owner lock)))
(cond ((not other-thread))
:thread *current-thread*
:cycle chain)))
(t
- (let ((other-lock (thread-waiting-for other-thread)))
+ (let ((other-lock (progn
+ (barrier (:read))
+ (thread-waiting-for other-thread))))
;; If the thread is waiting with a timeout OTHER-LOCK
;; is a cons, and we don't consider it a deadlock -- since
;; it will time out on its own sooner or later.
(deadlock-chain (thread lock)
(let* ((other-thread (mutex-owner lock))
(other-lock (when other-thread
+ (barrier (:read))
(thread-waiting-for other-thread))))
(cond ((not other-thread)
;; The deadlock is gone -- maybe someone unwound
(when old
(error "Strange deadlock on ~S in an unithreaded build?" mutex))
#!-sb-futex
- (and (not (mutex-%owner mutex))
+ (and (not old)
+ ;; Don't even bother to try to CAS if it looks bad.
(not (sb!ext:compare-and-swap (mutex-%owner mutex) nil new-owner)))
#!+sb-futex
;; From the Mutex 2 algorithm from "Futexes are Tricky" by Ulrich Drepper.
(declare (ignore to-sec to-usec))
#!-sb-futex
(flet ((cas ()
- (loop repeat 24
- when (and (not (mutex-%owner mutex))
+ (loop repeat 100
+ when (and (progn
+ (barrier (:read))
+ (not (mutex-%owner mutex)))
(not (sb!ext:compare-and-swap (mutex-%owner mutex) nil
new-owner)))
- do (return-from cas t))
+ do (return-from cas t)
+ else
+ do
+ (sb!ext:spin-loop-hint))
;; Check for pending interrupts.
(with-interrupts nil)))
(declare (dynamic-extent #'cas))
(setf (waitqueue-%head queue) (cdr head)))
(car head)))
while next
- do (when (eq queue (sb!ext:compare-and-swap (thread-waiting-for next) queue nil))
+ do (when (eq queue (sb!ext:compare-and-swap
+ (thread-waiting-for next) queue nil))
(decf n)))
nil))
(progn
#!-sb-futex
(progn
- (%waitqueue-enqueue me queue)
+ (%with-cas-lock ((waitqueue-%owner queue))
+ (%waitqueue-enqueue me queue))
(release-mutex mutex)
(setf status
(or (flet ((wakeup ()
- (when (neq queue (thread-waiting-for me))
+ (barrier (:read))
+ (when (neq queue
+ (thread-waiting-for me))
:ok)))
(declare (dynamic-extent #'wakeup))
(allow-with-interrupts