+;;; Of the WITH-PINNED-OBJECTS in this file, not every single one is
+;;; necessary because threads are only supported with the conservative
+;;; gencgc and numbers on the stack (returned by GET-LISP-OBJ-ADDRESS)
+;;; are treated as references.
+
+;;; set the doc here because in early-thread FDOCUMENTATION is not
+;;; available, yet
+#!+sb-doc
+(setf (sb!kernel:fdocumentation '*current-thread* 'variable)
+ "Bound in each thread to the thread itself.")
+
+(defstruct (thread (:constructor %make-thread))
+ #!+sb-doc
+ "Thread type. Do not rely on threads being structs as it may change
+in future versions."
+ name
+ %alive-p
+ os-thread
+ interruptions
+ (interruptions-lock (make-mutex :name "thread interruptions lock")))
+
+#!+sb-doc
+(setf (sb!kernel:fdocumentation 'thread-name 'function)
+ "The name of the thread. Setfable.")
+
+(def!method print-object ((thread thread) stream)
+ (if (thread-name thread)
+ (print-unreadable-object (thread stream :type t :identity t)
+ (prin1 (thread-name thread) stream))
+ (print-unreadable-object (thread stream :type t :identity t)
+ ;; body is empty => there is only one space between type and
+ ;; identity
+ ))
+ thread)
+
+(defun thread-alive-p (thread)
+ #!+sb-doc
+ "Check if THREAD is running."
+ (thread-%alive-p thread))
+
+;; A thread is eligible for gc iff it has finished and there are no
+;; more references to it. This list is supposed to keep a reference to
+;; all running threads.
+(defvar *all-threads* ())
+(defvar *all-threads-lock* (make-mutex :name "all threads lock"))
+
+(defun list-all-threads ()
+ #!+sb-doc
+ "Return a list of the live threads."
+ (with-mutex (*all-threads-lock*)
+ (copy-list *all-threads*)))
+
+(declaim (inline current-thread-sap))
+(defun current-thread-sap ()
+ (sb!vm::current-thread-offset-sap sb!vm::thread-this-slot))
+
+(declaim (inline current-thread-sap-id))
+(defun current-thread-sap-id ()
+ (sap-int
+ (sb!vm::current-thread-offset-sap sb!vm::thread-os-thread-slot)))
+
+(defun init-initial-thread ()
+ (/show0 "Entering INIT-INITIAL-THREAD")
+ (let ((initial-thread (%make-thread :name "initial thread"
+ :%alive-p t
+ :os-thread (current-thread-sap-id))))
+ (setq *current-thread* initial-thread)
+ ;; Either *all-threads* is empty or it contains exactly one thread
+ ;; in case we are in reinit since saving core with multiple
+ ;; threads doesn't work.
+ (setq *all-threads* (list initial-thread))))
+
+;;;;
+
+#!+sb-thread
+(progn
+ ;; FIXME it would be good to define what a thread id is or isn't
+ ;; (our current assumption is that it's a fixnum). It so happens
+ ;; that on Linux it's a pid, but it might not be on posix thread
+ ;; implementations.
+ (define-alien-routine ("create_thread" %create-thread)
+ unsigned-long (lisp-fun-address unsigned-long))
+
+ (define-alien-routine "signal_interrupt_thread"
+ integer (os-thread unsigned-long))
+
+ (define-alien-routine "block_blockable_signals"
+ void)
+
+ #!+sb-lutex
+ (progn
+ (declaim (inline %lutex-init %lutex-wait %lutex-wake
+ %lutex-lock %lutex-unlock))
+
+ (sb!alien:define-alien-routine ("lutex_init" %lutex-init)
+ int (lutex unsigned-long))
+
+ (sb!alien:define-alien-routine ("lutex_wait" %lutex-wait)
+ int (queue-lutex unsigned-long) (mutex-lutex unsigned-long))
+
+ (sb!alien:define-alien-routine ("lutex_wake" %lutex-wake)
+ int (lutex unsigned-long) (n int))
+
+ (sb!alien:define-alien-routine ("lutex_lock" %lutex-lock)
+ int (lutex unsigned-long))
+
+ (sb!alien:define-alien-routine ("lutex_unlock" %lutex-unlock)
+ int (lutex unsigned-long))
+
+ (sb!alien:define-alien-routine ("lutex_destroy" %lutex-destroy)
+ int (lutex unsigned-long))
+
+ ;; FIXME: Defining a whole bunch of alien-type machinery just for
+ ;; passing primitive lutex objects directly to foreign functions
+ ;; doesn't seem like fun right now. So instead we just manually
+ ;; pin the lutex, get its address, and let the callee untag it.
+ (defmacro with-lutex-address ((name lutex) &body body)
+ `(let ((,name ,lutex))
+ (with-pinned-objects (,name)
+ (let ((,name (sb!kernel:get-lisp-obj-address ,name)))
+ ,@body))))
+
+ (defun make-lutex ()
+ (/show0 "Entering MAKE-LUTEX")
+ ;; Suppress GC until the lutex has been properly registered with
+ ;; the GC.
+ (without-gcing
+ (let ((lutex (sb!vm::%make-lutex)))
+ (/show0 "LUTEX=..")
+ (/hexstr lutex)
+ (with-lutex-address (lutex lutex)
+ (%lutex-init lutex))
+ lutex))))
+
+ #!-sb-lutex
+ (progn
+ (declaim (inline futex-wait futex-wake))
+
+ (sb!alien:define-alien-routine "futex_wait"
+ int (word unsigned-long) (old-value unsigned-long))
+
+ (sb!alien:define-alien-routine "futex_wake"
+ int (word unsigned-long) (n unsigned-long))))
+
+;;; used by debug-int.lisp to access interrupt contexts
+#!-(and sb-fluid sb-thread) (declaim (inline sb!vm::current-thread-offset-sap))
+#!-sb-thread
+(defun sb!vm::current-thread-offset-sap (n)
+ (declare (type (unsigned-byte 27) n))
+ (sap-ref-sap (alien-sap (extern-alien "all_threads" (* t)))
+ (* n sb!vm:n-word-bytes)))
+
+;;;; spinlocks
+
+(declaim (inline get-spinlock release-spinlock))
+
+;;; The bare 2 here and below are offsets of the slots in the struct.
+;;; There ought to be some better way to get these numbers
+(defun get-spinlock (spinlock)
+ (declare (optimize (speed 3) (safety 0))
+ #!-sb-thread
+ (ignore spinlock new-value))
+ ;; %instance-set-conditional can test for 0 (which is a fixnum) and
+ ;; store any value
+ #!+sb-thread
+ (loop until
+ (eql (sb!vm::%instance-set-conditional spinlock 2 0 1) 0)))
+
+(defun release-spinlock (spinlock)
+ (declare (optimize (speed 3) (safety 0))
+ #!-sb-thread (ignore spinlock))
+ ;; %instance-set-conditional cannot compare arbitrary objects
+ ;; meaningfully, so
+ ;; (sb!vm::%instance-set-conditional spinlock 2 our-value 0)
+ ;; does not work for bignum thread ids.
+ #!+sb-thread
+ (sb!vm::%instance-set spinlock 2 0))
+
+(defmacro with-spinlock ((spinlock) &body body)
+ (sb!int:with-unique-names (lock)
+ `(let ((,lock ,spinlock))
+ (get-spinlock ,lock)
+ (unwind-protect
+ (progn ,@body)
+ (release-spinlock ,lock)))))
+
+;;;; mutexes
+
+#!+sb-doc
+(setf (sb!kernel:fdocumentation 'make-mutex 'function)
+ "Create a mutex."
+ (sb!kernel:fdocumentation 'mutex-name 'function)
+ "The name of the mutex. Setfable."
+ (sb!kernel:fdocumentation 'mutex-value 'function)
+ "The value of the mutex. NIL if the mutex is free. Setfable.")
+
+#!+(and sb-thread (not sb-lutex))
+(progn
+ (declaim (inline mutex-value-address))
+ (defun mutex-value-address (mutex)
+ (declare (optimize (speed 3)))
+ (sb!ext:truly-the
+ sb!vm:word
+ (+ (sb!kernel:get-lisp-obj-address mutex)
+ (- (* 3 sb!vm:n-word-bytes) sb!vm:instance-pointer-lowtag)))))
+
+(defun get-mutex (mutex &optional (new-value *current-thread*) (wait-p t))
+ #!+sb-doc
+ "Acquire MUTEX, 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"
+ (declare (type mutex mutex) (optimize (speed 3)))
+ (/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*))
+ ;; FIXME: sb-lutex and (not wait-p)
+ #!+sb-lutex
+ (when wait-p
+ (with-lutex-address (lutex (mutex-lutex mutex))
+ (%lutex-lock 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)
+ #!+sb-doc
+ "Release MUTEX by setting it to NIL. Wake up threads waiting for
+this mutex."
+ (declare (type mutex mutex))
+ (/show0 "Entering RELEASE-MUTEX")
+ (setf (mutex-value mutex) nil)
+ #!+sb-thread
+ (progn
+ #!+sb-lutex
+ (with-lutex-address (lutex (mutex-lutex mutex))
+ (%lutex-unlock lutex))
+ #!-sb-lutex
+ (futex-wake (mutex-value-address mutex) 1)))
+
+;;;; waitqueues/condition variables
+
+(defstruct (waitqueue (:constructor %make-waitqueue))
+ #!+sb-doc
+ "Waitqueue type."
+ (name nil :type (or null simple-string))
+ #!+(and sb-lutex sb-thread)
+ (lutex (make-lutex))
+ #!-sb-lutex