- (/show0 "Entering GET-MUTEX")
- (unless new-value
- (setq new-value *current-thread*))
- #!-sb-thread
- (let ((old-value (mutex-value mutex)))
- (when (and old-value wait-p)
- (error "In unithread mode, mutex ~S was requested with WAIT-P ~S and ~
- new-value ~S, but has already been acquired (with value ~S)."
- mutex wait-p new-value old-value))
- (setf (mutex-value mutex) new-value)
- t)
- #!+sb-thread
- (progn
- (when (eql new-value (mutex-value mutex))
- (warn "recursive lock attempt ~S~%" mutex)
- (format *debug-io* "Thread: ~A~%" *current-thread*)
- (sb!debug:backtrace most-positive-fixnum *debug-io*)
- (force-output *debug-io*))
- #!+sb-lutex
- (when (zerop (with-lutex-address (lutex (mutex-lutex mutex))
- (if wait-p
- (%lutex-lock lutex)
- (%lutex-trylock lutex))))
- (setf (mutex-value mutex) new-value))
- #!-sb-lutex
- (let (old)
- (loop
- (unless
- (setf old (sb!vm::%instance-set-conditional mutex 2 nil
- new-value))
- (return t))
- (unless wait-p (return nil))
- (with-pinned-objects (mutex old)
- (futex-wait (mutex-value-address mutex)
- (sb!kernel:get-lisp-obj-address old)))))))
-
-(defun release-mutex (mutex)
+ (barrier (:read))
+ (let ((old (mutex-%owner mutex)))
+ (when (eq new-owner old)
+ (error "Recursive lock attempt ~S." mutex))
+ #!-sb-thread
+ (when old
+ (error "Strange deadlock on ~S in an unithreaded build?" mutex))
+ #!-sb-futex
+ (and (not (mutex-%owner mutex))
+ (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.
+ (when (eql +lock-free+ (sb!ext:compare-and-swap (mutex-state mutex)
+ +lock-free+
+ +lock-taken+))
+ (let ((prev (sb!ext:compare-and-swap (mutex-%owner mutex) nil new-owner)))
+ (when prev
+ (bug "Old owner in free mutex: ~S" prev))
+ t))))
+
+#!+sb-thread
+(defun %%wait-for-mutex (mutex new-owner to-sec to-usec stop-sec stop-usec)
+ (declare (type mutex mutex) (optimize (speed 3)))
+ #!-sb-futex
+ (declare (ignore to-sec to-usec))
+ #!-sb-futex
+ (flet ((cas ()
+ (loop repeat 24
+ when (and (not (mutex-%owner mutex))
+ (not (sb!ext:compare-and-swap (mutex-%owner mutex) nil
+ new-owner)))
+ do (return-from cas t))
+ ;; Check for pending interrupts.
+ (with-interrupts nil)))
+ (declare (dynamic-extent #'cas))
+ (sb!impl::%%wait-for #'cas stop-sec stop-usec))
+ #!+sb-futex
+ ;; This is a fairly direct translation of the Mutex 2 algorithm from
+ ;; "Futexes are Tricky" by Ulrich Drepper.
+ (flet ((maybe (old)
+ (when (eql +lock-free+ old)
+ (let ((prev (sb!ext:compare-and-swap (mutex-%owner mutex)
+ nil new-owner)))
+ (when prev
+ (bug "Old owner in free mutex: ~S" prev))
+ (return-from %%wait-for-mutex t)))))
+ (prog ((old (sb!ext:compare-and-swap (mutex-state mutex)
+ +lock-free+ +lock-taken+)))
+ ;; Got it right off the bat?
+ (maybe old)
+ :retry
+ ;; Mark it as contested, and sleep. (Exception: it was just released.)
+ (when (or (eql +lock-contested+ old)
+ (not (eql +lock-free+
+ (sb!ext:compare-and-swap
+ (mutex-state mutex) +lock-taken+ +lock-contested+))))
+ (when (eql 1 (with-pinned-objects (mutex)
+ (futex-wait (mutex-state-address mutex)
+ (get-lisp-obj-address +lock-contested+)
+ (or to-sec -1)
+ (or to-usec 0))))
+ ;; -1 = EWOULDBLOCK, possibly spurious wakeup
+ ;; 0 = normal wakeup
+ ;; 1 = ETIMEDOUT ***DONE***
+ ;; 2 = EINTR, a spurious wakeup
+ (return-from %%wait-for-mutex nil)))
+ ;; Try to get it, still marking it as contested.
+ (maybe
+ (sb!ext:compare-and-swap (mutex-state mutex) +lock-free+ +lock-contested+))
+ ;; Update timeout if necessary.
+ (when stop-sec
+ (setf (values to-sec to-usec)
+ (sb!impl::relative-decoded-times stop-sec stop-usec)))
+ ;; Spin.
+ (go :retry))))
+
+(defun %wait-for-mutex (mutex self timeout to-sec to-usec stop-sec stop-usec deadlinep)
+ (with-deadlocks (self mutex timeout)
+ (with-interrupts (check-deadlock))
+ (tagbody
+ :again
+ (return-from %wait-for-mutex
+ (or (%%wait-for-mutex mutex self to-sec to-usec stop-sec stop-usec)
+ (when deadlinep
+ (signal-deadline)
+ ;; FIXME: substract elapsed time from timeout...
+ (setf (values to-sec to-usec stop-sec stop-usec deadlinep)
+ (decode-timeout timeout))
+ (go :again)))))))
+
+(defun get-mutex (mutex &optional new-owner (waitp t) (timeout nil))
+ #!+sb-doc
+ "Deprecated in favor of GRAB-MUTEX."
+ (declare (ignorable waitp timeout))
+ (let ((new-owner (or new-owner *current-thread*)))
+ (or (%try-mutex mutex new-owner)
+ #!+sb-thread
+ (when waitp
+ (multiple-value-call #'%wait-for-mutex
+ mutex new-owner timeout (decode-timeout timeout))))))
+
+(defun grab-mutex (mutex &key (waitp t) (timeout nil))
+ #!+sb-doc
+ "Acquire MUTEX for the current thread. If WAITP is true (the default) and
+the mutex is not immediately available, sleep until it is available.
+
+If TIMEOUT is given, it specifies a relative timeout, in seconds, on how long
+GRAB-MUTEX should try to acquire the lock in the contested case.
+
+If GRAB-MUTEX returns T, the lock acquisition was successful. In case of WAITP
+being NIL, or an expired TIMEOUT, GRAB-MUTEX may also return NIL which denotes
+that GRAB-MUTEX did -not- acquire the lock.
+
+Notes:
+
+ - GRAB-MUTEX is not interrupt safe. The correct way to call it is:
+
+ (WITHOUT-INTERRUPTS
+ ...
+ (ALLOW-WITH-INTERRUPTS (GRAB-MUTEX ...))
+ ...)
+
+ WITHOUT-INTERRUPTS is necessary to avoid an interrupt unwinding the call
+ while the mutex is in an inconsistent state while ALLOW-WITH-INTERRUPTS
+ allows the call to be interrupted from sleep.
+
+ - (GRAB-MUTEX <mutex> :timeout 0.0) differs from
+ (GRAB-MUTEX <mutex> :waitp nil) in that the former may signal a
+ DEADLINE-TIMEOUT if the global deadline was due already on entering
+ GRAB-MUTEX.
+
+ The exact interplay of GRAB-MUTEX and deadlines are reserved to change in
+ future versions.
+
+ - It is recommended that you use WITH-MUTEX instead of calling GRAB-MUTEX
+ directly.
+"
+ (declare (ignorable waitp timeout))
+ (let ((self *current-thread*))
+ (or (%try-mutex mutex self)
+ #!+sb-thread
+ (when waitp
+ (multiple-value-call #'%wait-for-mutex
+ mutex self timeout (decode-timeout timeout))))))
+
+(defun release-mutex (mutex &key (if-not-owner :punt))