X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fthread.lisp;h=5dfb84e6eb6c0feaf5aac3d3b23193c84e0c2c5f;hb=5abf3b4b94c8c2315777e63729293395dc54992c;hp=d327ce9b92db7476a1bc543c04d26c04992c65f7;hpb=5d811ef35f080723cfe2aacd128db320620c759c;p=sbcl.git diff --git a/src/code/thread.lisp b/src/code/thread.lisp index d327ce9..5dfb84e 100644 --- a/src/code/thread.lisp +++ b/src/code/thread.lisp @@ -11,45 +11,117 @@ (in-package "SB!THREAD") +(def!type thread-name () + 'simple-string) + +(def!struct (thread (:constructor %make-thread)) + #!+sb-doc + "Thread type. Do not rely on threads being structs as it may change +in future versions." + (name nil :type (or thread-name null)) + (%alive-p nil :type boolean) + (os-thread nil :type (or integer null)) + (interruptions nil :type list) + (result nil :type list) + (interruptions-lock + (make-mutex :name "thread interruptions lock") + :type mutex) + (result-lock + (make-mutex :name "thread result lock") + :type mutex) + waiting-for) + (def!struct mutex #!+sb-doc "Mutex type." - (name nil :type (or null simple-string)) - (value nil) + (name nil :type (or null thread-name)) + (%owner nil :type (or null thread)) + #!+(and (not sb-lutex) sb-thread) + (state 0 :type fixnum) #!+(and sb-lutex sb-thread) (lutex (make-lutex))) +(defun mutex-value (mutex) + "Current owner of the mutex, NIL if the mutex is free. May return a +stale value, use MUTEX-OWNER instead." + (mutex-%owner mutex)) + +(defun holding-mutex-p (mutex) + "Test whether the current thread is holding MUTEX." + ;; This is about the only use for which a stale value of owner is + ;; sufficient. + (eq sb!thread:*current-thread* (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 0)) + (name nil :type (or null thread-name)) + (value nil)) + +(sb!xc:defmacro without-thread-waiting-for ((&key already-without-interrupts) &body body) + (with-unique-names (thread prev) + (let ((without (if already-without-interrupts + 'progn + 'without-interrupts)) + (with (if already-without-interrupts + 'progn + 'with-local-interrupts))) + `(let* ((,thread *current-thread*) + (,prev (thread-waiting-for ,thread))) + (flet ((exec () ,@body)) + (if ,prev + (,without + (unwind-protect + (progn + (setf (thread-waiting-for ,thread) nil) + (,with (exec))) + (setf (thread-waiting-for ,thread) ,prev))) + (exec))))))) (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" - #!-sb-thread (declare (ignore mutex value wait-p)) - #!+sb-thread - (with-unique-names (got mutex1) - `(let ((,mutex1 ,mutex) - ,got) - (/show0 "WITH-MUTEX") - (unwind-protect - ;; FIXME: async unwind in SETQ form - (when (setq ,got (get-mutex ,mutex1 ,value ,wait-p)) - (locally - ,@body)) - (when ,got - (release-mutex ,mutex1))))) - ;; KLUDGE: this separate expansion 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 - `(locally ,@body)) + "Acquire MUTEX for the dynamic scope of BODY, setting it to 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 @@ -57,35 +129,175 @@ and the mutex is in use, sleep until it is available" 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." - #!-sb-thread - (declare (ignore mutex)) - #!+sb-thread - (with-unique-names (mutex1 inner-lock-p) - `(let* ((,mutex1 ,mutex) - (,inner-lock-p (eq (mutex-value ,mutex1) *current-thread*))) - (unwind-protect - (progn - (unless ,inner-lock-p - (get-mutex ,mutex1)) - (locally - ,@body)) - (unless ,inner-lock-p - (release-mutex ,mutex1))))) - #!-sb-thread - `(locally ,@body)) + `(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) - #!-sb-thread - (declare (ignore spinlock)) - #!-sb-thread - `(locally ,@body) - #!+sb-thread - (with-unique-names (lock got-it) - `(let ((,lock ,spinlock) - (,got-it nil)) - (unwind-protect - (progn - (setf ,got-it (get-spinlock ,lock)) - (locally ,@body)) - (when ,got-it - (release-spinlock ,lock)))))) + `(dx-flet ((with-spinlock-thunk () ,@body)) + (call-with-spinlock + #'with-spinlock-thunk + ,spinlock))) + +(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)) + +#!-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-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 + (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)))))))