+\f
+
+;;;; The beef
+
+(defun make-thread (function &key name)
+ #!+sb-doc
+ "Create a new thread of NAME that runs FUNCTION. When the function
+returns the thread exits. The return values of FUNCTION are kept
+around and can be retrieved by JOIN-THREAD."
+ #!-sb-thread (declare (ignore function name))
+ #!-sb-thread (error "Not supported in unithread builds.")
+ #!+sb-thread
+ (let* ((thread (%make-thread :name name))
+ (setup-sem (make-semaphore :name "Thread setup semaphore"))
+ (real-function (coerce function 'function))
+ (initial-function
+ (lambda ()
+ ;; In time we'll move some of the binding presently done in C
+ ;; here too.
+ ;;
+ ;; KLUDGE: Here we have a magic list of variables that are
+ ;; not thread-safe for one reason or another. As people
+ ;; report problems with the thread safety of certain
+ ;; variables, (e.g. "*print-case* in multiple threads
+ ;; broken", sbcl-devel 2006-07-14), we add a few more
+ ;; bindings here. The Right Thing is probably some variant
+ ;; of Allegro's *cl-default-special-bindings*, as that is at
+ ;; least accessible to users to secure their own libraries.
+ ;; --njf, 2006-07-15
+ ;;
+ ;; As it is, this lambda must not cons until we are ready
+ ;; to run GC. Be very careful.
+ (let* ((*current-thread* thread)
+ (*restart-clusters* nil)
+ (*handler-clusters* (sb!kernel::initial-handler-clusters))
+ (*condition-restarts* nil)
+ (sb!impl::*deadline* nil)
+ (sb!impl::*step-out* nil)
+ ;; internal printer variables
+ (sb!impl::*previous-case* nil)
+ (sb!impl::*previous-readtable-case* nil)
+ (empty (vector))
+ (sb!impl::*merge-sort-temp-vector* empty)
+ (sb!impl::*zap-array-data-temp* empty)
+ (sb!impl::*internal-symbol-output-fun* nil)
+ (sb!impl::*descriptor-handlers* nil)) ; serve-event
+ ;; Binding from C
+ (setf sb!vm:*alloc-signal* *default-alloc-signal*)
+ (setf (thread-os-thread thread) (current-thread-os-thread))
+ (with-mutex ((thread-result-lock thread))
+ (with-all-threads-lock
+ (push thread *all-threads*))
+ (with-session-lock (*session*)
+ (push thread (session-threads *session*)))
+ (setf (thread-%alive-p thread) t)
+ (signal-semaphore setup-sem)
+ ;; can't use handling-end-of-the-world, because that flushes
+ ;; output streams, and we don't necessarily have any (or we
+ ;; could be sharing them)
+ (catch 'sb!impl::toplevel-catcher
+ (catch 'sb!impl::%end-of-the-world
+ (with-simple-restart
+ (terminate-thread
+ (format nil
+ "~~@<Terminate this thread (~A)~~@:>"
+ *current-thread*))
+ (without-interrupts
+ (unwind-protect
+ (with-local-interrupts
+ ;; Now that most things have a chance
+ ;; to work properly without messing up
+ ;; other threads, it's time to enable
+ ;; signals.
+ (sb!unix::unblock-deferrable-signals)
+ (setf (thread-result thread)
+ (cons t
+ (multiple-value-list
+ (funcall real-function))))
+ ;; Try to block deferrables. An
+ ;; interrupt may unwind it, but for a
+ ;; normal exit it prevents interrupt
+ ;; loss.
+ (block-deferrable-signals))
+ ;; We're going down, can't handle interrupts
+ ;; sanely anymore. GC remains enabled.
+ (block-deferrable-signals)
+ ;; We don't want to run interrupts in a dead
+ ;; thread when we leave WITHOUT-INTERRUPTS.
+ ;; This potentially causes important
+ ;; interupts to be lost: SIGINT comes to
+ ;; mind.
+ (setq *interrupt-pending* nil)
+ (handle-thread-exit thread))))))))
+ (values))))
+ ;; If the starting thread is stopped for gc before it signals the
+ ;; semaphore then we'd be stuck.
+ (assert (not *gc-inhibit*))
+ ;; Keep INITIAL-FUNCTION pinned until the child thread is
+ ;; initialized properly. Wrap the whole thing in
+ ;; WITHOUT-INTERRUPTS because we pass INITIAL-FUNCTION to another
+ ;; thread.
+ (without-interrupts
+ (with-pinned-objects (initial-function)
+ (let ((os-thread
+ (%create-thread
+ (get-lisp-obj-address initial-function))))
+ (when (zerop os-thread)
+ (error "Can't create a new thread"))
+ (wait-on-semaphore setup-sem)
+ thread)))))
+
+(define-condition join-thread-error (error)
+ ((thread :reader join-thread-error-thread :initarg :thread))
+ #!+sb-doc
+ (:documentation "Joining thread failed.")
+ (:report (lambda (c s)
+ (format s "Joining thread failed: thread ~A ~
+ has not returned normally."
+ (join-thread-error-thread c)))))
+
+#!+sb-doc
+(setf (fdocumentation 'join-thread-error-thread 'function)
+ "The thread that we failed to join.")
+
+(defun join-thread (thread &key (default nil defaultp))
+ #!+sb-doc
+ "Suspend current thread until THREAD exits. Returns the result
+values of the thread function. If the thread does not exit normally,
+return DEFAULT if given or else signal JOIN-THREAD-ERROR."
+ (with-system-mutex ((thread-result-lock thread) :allow-with-interrupts t)
+ (cond ((car (thread-result thread))
+ (return-from join-thread
+ (values-list (cdr (thread-result thread)))))
+ (defaultp
+ (return-from join-thread default))))
+ (error 'join-thread-error :thread thread))
+
+(defun destroy-thread (thread)
+ #!+sb-doc
+ "Deprecated. Same as TERMINATE-THREAD."
+ (terminate-thread thread))
+
+(define-condition interrupt-thread-error (error)
+ ((thread :reader interrupt-thread-error-thread :initarg :thread))
+ #!+sb-doc
+ (:documentation "Interrupting thread failed.")
+ (:report (lambda (c s)
+ (format s "Interrupt thread failed: thread ~A has exited."
+ (interrupt-thread-error-thread c)))))
+
+#!+sb-doc
+(setf (fdocumentation 'interrupt-thread-error-thread 'function)
+ "The thread that was not interrupted.")
+
+(defmacro with-interruptions-lock ((thread) &body body)
+ `(with-system-mutex ((thread-interruptions-lock ,thread))
+ ,@body))
+
+;;; Called from the signal handler.
+#!-win32
+(defun run-interruption ()
+ (let ((interruption (with-interruptions-lock (*current-thread*)
+ (pop (thread-interruptions *current-thread*)))))
+ ;; If there is more to do, then resignal and let the normal
+ ;; interrupt deferral mechanism take care of the rest. From the
+ ;; OS's point of view the signal we are in the handler for is no
+ ;; longer pending, so the signal will not be lost.
+ (when (thread-interruptions *current-thread*)
+ (kill-safely (thread-os-thread *current-thread*) sb!unix:sigpipe))
+ (when interruption
+ (funcall interruption))))
+
+(defun interrupt-thread (thread function)
+ #!+sb-doc
+ "Interrupt the live THREAD and make it run FUNCTION. A moderate
+degree of care is expected for use of INTERRUPT-THREAD, due to its
+nature: if you interrupt a thread that was holding important locks
+then do something that turns out to need those locks, you probably
+won't like the effect. FUNCTION runs with interrupts disabled, but
+WITH-INTERRUPTS is allowed in it. Keep in mind that many things may
+enable interrupts (GET-MUTEX when contended, for instance) so the
+first thing to do is usually a WITH-INTERRUPTS or a
+WITHOUT-INTERRUPTS. Within a thread interrupts are queued, they are
+run in same the order they were sent."
+ #!+win32
+ (declare (ignore thread))
+ #!+win32
+ (with-interrupt-bindings
+ (with-interrupts (funcall function)))
+ #!-win32
+ (let ((os-thread (thread-os-thread thread)))
+ (cond ((not os-thread)
+ (error 'interrupt-thread-error :thread thread))
+ (t
+ (with-interruptions-lock (thread)
+ ;; Append to the end of the interruptions queue. It's
+ ;; O(N), but it does not hurt to slow interruptors down a
+ ;; bit when the queue gets long.
+ (setf (thread-interruptions thread)
+ (append (thread-interruptions thread)
+ (list (lambda ()
+ (without-interrupts
+ (allow-with-interrupts
+ (funcall function))))))))
+ (when (minusp (kill-safely os-thread sb!unix:sigpipe))
+ (error 'interrupt-thread-error :thread thread))))))
+
+(defun terminate-thread (thread)
+ #!+sb-doc
+ "Terminate the thread identified by THREAD, by causing it to run
+SB-EXT:QUIT - the usual cleanup forms will be evaluated"
+ (interrupt-thread thread 'sb!ext:quit))
+
+(define-alien-routine "thread_yield" int)
+
+#!+sb-doc
+(setf (fdocumentation 'thread-yield 'function)
+ "Yield the processor to other threads.")
+
+;;; internal use only. If you think you need to use these, either you
+;;; are an SBCL developer, are doing something that you should discuss
+;;; with an SBCL developer first, or are doing something that you
+;;; should probably discuss with a professional psychiatrist first
+#!+sb-thread
+(progn
+ (defun %thread-sap (thread)
+ (let ((thread-sap (alien-sap (extern-alien "all_threads" (* t))))
+ (target (thread-os-thread thread)))
+ (loop
+ (when (sap= thread-sap (int-sap 0)) (return nil))
+ (let ((os-thread (sap-ref-word thread-sap
+ (* sb!vm:n-word-bytes
+ sb!vm::thread-os-thread-slot))))
+ (when (= os-thread target) (return thread-sap))
+ (setf thread-sap
+ (sap-ref-sap thread-sap (* sb!vm:n-word-bytes
+ sb!vm::thread-next-slot)))))))
+
+ (defun %symbol-value-in-thread (symbol thread)
+ (tagbody
+ ;; Prevent the dead from dying completely while we look for the
+ ;; TLS area...
+ (with-all-threads-lock
+ (if (thread-alive-p thread)
+ (let* ((offset (* sb!vm:n-word-bytes
+ (sb!vm::symbol-tls-index symbol)))
+ (tl-val (sap-ref-word (%thread-sap thread) offset)))
+ (if (eql tl-val sb!vm::no-tls-value-marker-widetag)
+ (go :unbound)
+ (return-from %symbol-value-in-thread
+ (values (make-lisp-obj tl-val) t))))
+ (return-from %symbol-value-in-thread (values nil nil))))
+ :unbound
+ (error "Cannot read thread-local symbol value: ~S unbound in ~S"
+ symbol thread)))
+
+ (defun %set-symbol-value-in-thread (symbol thread value)
+ (tagbody
+ (with-pinned-objects (value)
+ ;; Prevent the dead from dying completely while we look for
+ ;; the TLS area...
+ (with-all-threads-lock
+ (if (thread-alive-p thread)
+ (let* ((offset (* sb!vm:n-word-bytes
+ (sb!vm::symbol-tls-index symbol)))
+ (sap (%thread-sap thread))
+ (tl-val (sap-ref-word sap offset)))
+ (if (eql tl-val sb!vm::no-tls-value-marker-widetag)
+ (go :unbound)
+ (setf (sap-ref-word sap offset)
+ (get-lisp-obj-address value)))
+ (return-from %set-symbol-value-in-thread (values value t)))
+ (return-from %set-symbol-value-in-thread (values nil nil)))))
+ :unbound
+ (error "Cannot set thread-local symbol value: ~S unbound in ~S"
+ symbol thread))))
+
+(defun sb!vm::locked-symbol-global-value-add (symbol-name delta)
+ (sb!vm::locked-symbol-global-value-add symbol-name delta))
+\f
+
+;;;; Stepping
+
+(defun thread-stepping ()
+ (make-lisp-obj
+ (sap-ref-word (current-thread-sap)
+ (* sb!vm::thread-stepping-slot sb!vm:n-word-bytes))))
+
+(defun (setf thread-stepping) (value)
+ (setf (sap-ref-word (current-thread-sap)
+ (* sb!vm::thread-stepping-slot sb!vm:n-word-bytes))
+ (get-lisp-obj-address value)))