;;;; support for threads needed at cross-compile time ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; ;;;; This software is derived from the CMU CL system, which was ;;;; written at Carnegie Mellon University and released into the ;;;; public domain. The software is in the public domain and is ;;;; provided with absolutely no warranty. See the COPYING and CREDITS ;;;; files for more information. (in-package "SB!THREAD") (def!struct mutex #!+sb-doc "Mutex type." (name nil :type (or null simple-string)) (%owner nil :type (or null thread)) #!+(and (not sb-lutex) sb-thread) (state 0 :type fixnum) #!+(and sb-lutex sb-thread) (lutex (make-lutex))) ;;; FIXME: We probably want to rename the accessor MUTEX-OWNER. (defun mutex-value (mutex) "Current owner of the mutex, NIL if the mutex is free." (mutex-%owner mutex)) (defsetf mutex-value set-mutex-value) (declaim (inline set-mutex-value)) (defun set-mutex-value (mutex value) (declare (ignore mutex value)) (error "~S is no longer supported." '(setf mutex-value))) (define-compiler-macro set-mutex-value (&whole form mutex value) (declare (ignore mutex value)) (warn "~S is no longer supported, and will signal an error at runtime." '(setf mutex-value)) form) (def!struct spinlock #!+sb-doc "Spinlock type." (name nil :type (or null simple-string)) (value nil)) (sb!xc:defmacro with-mutex ((mutex &key (value '*current-thread*) (wait-p t)) &body body) #!+sb-doc "Acquire MUTEX for the dynamic scope of BODY, setting it to NEW-VALUE or some suitable default value if NIL. If WAIT-P is non-NIL and the mutex is in use, sleep until it is available" `(dx-flet ((with-mutex-thunk () ,@body)) (call-with-mutex #'with-mutex-thunk ,mutex ,value ,wait-p))) (sb!xc:defmacro with-system-mutex ((mutex &key without-gcing allow-with-interrupts) &body body) `(dx-flet ((with-system-mutex-thunk () ,@body)) (,(cond (without-gcing 'call-with-system-mutex/without-gcing) (allow-with-interrupts 'call-with-system-mutex/allow-with-interrupts) (t 'call-with-system-mutex)) #'with-system-mutex-thunk ,mutex))) (sb!xc:defmacro with-system-spinlock ((spinlock &key) &body body) `(dx-flet ((with-system-spinlock-thunk () ,@body)) (call-with-system-spinlock #'with-system-spinlock-thunk ,spinlock))) (sb!xc:defmacro with-recursive-lock ((mutex) &body body) #!+sb-doc "Acquires MUTEX for the dynamic scope of BODY. Within that scope further recursive lock attempts for the same mutex succeed. It is allowed to mix WITH-MUTEX and WITH-RECURSIVE-LOCK for the same mutex provided the default value is used for the mutex." `(dx-flet ((with-recursive-lock-thunk () ,@body)) (call-with-recursive-lock #'with-recursive-lock-thunk ,mutex))) (sb!xc:defmacro with-recursive-spinlock ((spinlock) &body body) `(dx-flet ((with-recursive-spinlock-thunk () ,@body)) (call-with-recursive-spinlock #'with-recursive-spinlock-thunk ,spinlock))) (sb!xc:defmacro with-recursive-system-spinlock ((spinlock &key without-gcing) &body body) `(dx-flet ((with-recursive-system-spinlock-thunk () ,@body)) (,(cond (without-gcing 'call-with-recursive-system-spinlock/without-gcing) (t 'call-with-recursive-system-spinlock)) #'with-recursive-system-spinlock-thunk ,spinlock))) (sb!xc:defmacro with-spinlock ((spinlock) &body body) `(dx-flet ((with-spinlock-thunk () ,@body)) (call-with-spinlock #'with-spinlock-thunk ,spinlock))) ;;; KLUDGE: this separate implementation for (NOT SB-THREAD) is not ;;; strictly necessary; GET-MUTEX and RELEASE-MUTEX are implemented. ;;; However, there would be a (possibly slight) performance hit in ;;; using them. #!-sb-thread (progn (macrolet ((def (name &optional variant) `(defun ,(if variant (symbolicate name "/" variant) name) (function lock) (declare (ignore lock) (function function)) ,(ecase variant (:without-gcing `(without-gcing (funcall function))) (:allow-with-interrupts `(without-interrupts (allow-with-interrupts (funcall function)))) ((nil) `(without-interrupts (funcall function))))))) (def call-with-system-mutex) (def call-with-system-mutex :without-gcing) (def call-with-system-mutex :allow-with-interrupts) (def call-with-system-spinlock) (def call-with-recursive-system-spinlock) (def call-with-recursive-system-spinlock :without-gcing)) (defun call-with-mutex (function mutex value waitp) (declare (ignore mutex value waitp) (function function)) (funcall function)) (defun call-with-recursive-lock (function mutex) (declare (ignore mutex) (function function)) (funcall function)) (defun call-with-spinlock (function spinlock) (declare (ignore spinlock) (function function)) (funcall function)) (defun call-with-recursive-spinlock (function spinlock) (declare (ignore spinlock) (function function)) (funcall function))) #!+sb-thread ;;; KLUDGE: These need to use DX-LET, because the cleanup form that ;;; closes over GOT-IT causes a value-cell to be allocated for it -- ;;; and we prefer that to go on the stack since it can. (progn (macrolet ((def (name &optional variant) `(defun ,(if variant (symbolicate name "/" variant) name) (function mutex) (declare (function function)) (flet ((%call-with-system-mutex () (dx-let (got-it) (unwind-protect (when (setf got-it (get-mutex mutex)) (funcall function)) (when got-it (release-mutex mutex)))))) (declare (inline %call-with-system-mutex)) ,(ecase variant (:without-gcing `(without-gcing (%call-with-system-mutex))) (:allow-with-interrupts `(without-interrupts (allow-with-interrupts (%call-with-system-mutex)))) ((nil) `(without-interrupts (%call-with-system-mutex)))))))) (def call-with-system-mutex) (def call-with-system-mutex :without-gcing) (def call-with-system-mutex :allow-with-interrupts)) (defun call-with-system-spinlock (function spinlock) (declare (function function)) (without-interrupts (dx-let (got-it) (unwind-protect (when (setf got-it (get-spinlock spinlock)) (funcall function)) (when got-it (release-spinlock spinlock)))))) (macrolet ((def (name &optional variant) `(defun ,(if variant (symbolicate name "/" variant) name) (function spinlock) (declare (function function)) (flet ((%call-with-system-spinlock () (dx-let ((inner-lock-p (eq *current-thread* (spinlock-value spinlock))) (got-it nil)) (unwind-protect (when (or inner-lock-p (setf got-it (get-spinlock spinlock))) (funcall function)) (when got-it (release-spinlock spinlock)))))) (declare (inline %call-with-system-spinlock)) ,(ecase variant (:without-gcing `(without-gcing (%call-with-system-spinlock))) ((nil) `(without-interrupts (%call-with-system-spinlock)))))))) (def call-with-recursive-system-spinlock) (def call-with-recursive-system-spinlock :without-gcing)) (defun call-with-spinlock (function spinlock) (declare (function function)) (dx-let ((got-it nil)) (without-interrupts (unwind-protect (when (setf got-it (allow-with-interrupts (get-spinlock spinlock))) (with-local-interrupts (funcall function))) (when got-it (release-spinlock spinlock)))))) (defun call-with-mutex (function mutex value waitp) (declare (function function)) (dx-let ((got-it nil)) (without-interrupts (unwind-protect (when (setq got-it (allow-with-interrupts (get-mutex mutex value waitp))) (with-local-interrupts (funcall function))) (when got-it (release-mutex mutex)))))) (defun call-with-recursive-lock (function mutex) (declare (function function)) (dx-let ((inner-lock-p (eq (mutex-%owner mutex) *current-thread*)) (got-it nil)) (without-interrupts (unwind-protect (when (or inner-lock-p (setf got-it (allow-with-interrupts (get-mutex mutex)))) (with-local-interrupts (funcall function))) (when got-it (release-mutex mutex)))))) (defun call-with-recursive-spinlock (function spinlock) (declare (function function)) (dx-let ((inner-lock-p (eq (spinlock-value spinlock) *current-thread*)) (got-it nil)) (without-interrupts (unwind-protect (when (or inner-lock-p (setf got-it (allow-with-interrupts (get-spinlock spinlock)))) (with-local-interrupts (funcall function))) (when got-it (release-spinlock spinlock)))))))