- (/show0 "CONDITION-WAITing")
- #!+sb-lutex
- ;; Need to disable interrupts so that we don't miss setting the
- ;; owner on our way out. (pthread_cond_wait handles the actual
- ;; re-acquisition.)
- (without-interrupts
- (unwind-protect
- (progn
- (setf (mutex-%owner mutex) nil)
- (with-lutex-address (queue-lutex-address (waitqueue-lutex queue))
- (with-lutex-address (mutex-lutex-address (mutex-lutex mutex))
- (with-local-interrupts
- (%lutex-wait queue-lutex-address mutex-lutex-address)))))
- (barrier (:write)
- (setf (mutex-%owner mutex) me))))
- #!-sb-lutex
- ;; Need to disable interrupts so that we don't miss grabbing the
- ;; mutex on our way out.
- (without-interrupts
- ;; This setf becomes visible to other CPUS due to the usual
- ;; memory barrier semantics of lock acquire/release. This must
- ;; not be moved into the loop else wakeups may be lost upon
- ;; continuing after a deadline or EINTR.
- (setf (waitqueue-token queue) me)
- (loop
- (multiple-value-bind (to-sec to-usec)
- (allow-with-interrupts (decode-timeout nil))
- (case (unwind-protect
- (with-pinned-objects (queue me)
- ;; RELEASE-MUTEX is purposefully as close to
- ;; FUTEX-WAIT as possible to reduce the size of
- ;; the window where the token may be set by a
- ;; notifier.
- (release-mutex mutex)
- ;; Now we go to sleep using futex-wait. If
- ;; anyone else manages to grab MUTEX and call
- ;; CONDITION-NOTIFY during this comment, it
- ;; will change the token, and so futex-wait
- ;; returns immediately instead of sleeping.
- ;; Ergo, no lost wakeup. We may get spurious
- ;; wakeups, but that's ok.
- (allow-with-interrupts
- (futex-wait (waitqueue-token-address queue)
- (get-lisp-obj-address me)
- ;; our way of saying "no
- ;; timeout":
- (or to-sec -1)
- (or to-usec 0))))
- ;; If we are interrupted while waiting, we should
- ;; do these things before returning. Ideally, in
- ;; the case of an unhandled signal, we should do
- ;; them before entering the debugger, but this is
- ;; better than nothing.
- (allow-with-interrupts (get-mutex mutex)))
- ;; ETIMEDOUT; we know it was a timeout, yet we cannot
- ;; signal a deadline unconditionally here because the
- ;; call to GET-MUTEX may already have signaled it.
- ((1))
- ;; EINTR; we do not need to return to the caller because
- ;; an interleaved wakeup would change the token causing an
- ;; EWOULDBLOCK in the next iteration.
- ((2))
- ;; EWOULDBLOCK, -1 here, is the possible spurious wakeup
- ;; case. 0 is the normal wakeup.
- (otherwise (return))))))))
+ (multiple-value-bind (to-sec to-usec stop-sec stop-usec deadlinep)
+ (decode-timeout timeout)
+ (let ((status :interrupted))
+ ;; Need to disable interrupts so that we don't miss grabbing the
+ ;; mutex on our way out.
+ (without-interrupts
+ (unwind-protect
+ (progn
+ #!-sb-futex
+ (progn
+ (%waitqueue-enqueue me queue)
+ (release-mutex mutex)
+ (setf status
+ (or (flet ((wakeup ()
+ (when (neq queue (thread-waiting-for me))
+ :ok)))
+ (declare (dynamic-extent #'wakeup))
+ (allow-with-interrupts
+ (sb!impl::%%wait-for #'wakeup stop-sec stop-usec)))
+ :timeout)))
+ #!+sb-futex
+ (with-pinned-objects (queue me)
+ (setf (waitqueue-token queue) me)
+ (release-mutex mutex)
+ ;; Now we go to sleep using futex-wait. If anyone else
+ ;; manages to grab MUTEX and call CONDITION-NOTIFY during
+ ;; this comment, it will change the token, and so futex-wait
+ ;; returns immediately instead of sleeping. Ergo, no lost
+ ;; wakeup. We may get spurious wakeups, but that's ok.
+ (setf status
+ (case (allow-with-interrupts
+ (futex-wait (waitqueue-token-address queue)
+ (get-lisp-obj-address me)
+ ;; our way of saying "no
+ ;; timeout":
+ (or to-sec -1)
+ (or to-usec 0)))
+ ((1)
+ ;; 1 = ETIMEDOUT
+ :timeout)
+ (t
+ ;; -1 = EWOULDBLOCK, possibly spurious wakeup
+ ;; 0 = normal wakeup
+ ;; 2 = EINTR, a spurious wakeup
+ :ok)))))
+ #!-sb-futex
+ (%with-cas-lock ((waitqueue-%owner queue))
+ (if (eq queue (thread-waiting-for me))
+ (%waitqueue-drop me queue)
+ (unless (eq :ok status)
+ ;; CONDITION-NOTIFY thinks we've been woken up, but really
+ ;; we're unwinding. Wake someone else up.
+ (%waitqueue-wakeup queue 1))))
+ ;; Update timeout for mutex re-aquisition.
+ (when (and (eq :ok status) to-sec)
+ (setf (values to-sec to-usec)
+ (sb!impl::relative-decoded-times stop-sec stop-usec)))
+ ;; If we ran into deadline, try to get the mutex before
+ ;; signaling. If we don't unwind it will look like a normal
+ ;; return from user perspective.
+ (when (and (eq :timeout status) deadlinep)
+ (let ((got-it (%try-mutex mutex me)))
+ (allow-with-interrupts
+ (signal-deadline))
+ (cond (got-it
+ (return-from condition-wait t))
+ (t
+ (setf (values to-sec to-usec stop-sec stop-usec deadlinep)
+ (decode-timeout timeout))))))
+ ;; Re-acquire the mutex for normal return.
+ (unless (or (%try-mutex mutex me)
+ (allow-with-interrupts
+ (%wait-for-mutex mutex me timeout
+ to-sec to-usec
+ stop-sec stop-usec deadlinep)))
+ ;; The only case we return normally without re-acquiring the
+ ;; mutex is when there is a :TIMEOUT that runs out.
+ (aver (and timeout (not deadlinep)))
+ (return-from condition-wait nil)))))))
+ t)