X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Ftarget-thread.lisp;h=359b0f3c89255c3a1d5ed04617fd57d718d071d0;hb=f12b298a4ce9090470000360b49523e56475a680;hp=ca746634fa746f602d05884c9c218e19e47e74f2;hpb=dc9fb9111cb1b645aaede0d3ec019c0f78200be0;p=sbcl.git diff --git a/src/code/target-thread.lisp b/src/code/target-thread.lisp index ca74663..359b0f3 100644 --- a/src/code/target-thread.lisp +++ b/src/code/target-thread.lisp @@ -87,8 +87,10 @@ in future versions." (declaim (inline current-thread-os-thread)) (defun current-thread-os-thread () - (sap-int - (sb!vm::current-thread-offset-sap sb!vm::thread-os-thread-slot))) + #!+sb-thread + (sap-int (sb!vm::current-thread-offset-sap sb!vm::thread-os-thread-slot)) + #!-sb-thread + 0) (defun init-initial-thread () (/show0 "Entering INIT-INITIAL-THREAD") @@ -104,6 +106,11 @@ in future versions." ;;;; Aliens, low level stuff +(define-alien-routine "kill_safely" + integer + (os-thread #!-alpha unsigned-long #!+alpha unsigned-int) + (signal int)) + #!+sb-thread (progn ;; FIXME it would be good to define what a thread id is or isn't @@ -113,9 +120,6 @@ in future versions." (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_deferrable_signals" void) @@ -216,10 +220,14 @@ in future versions." (if (and (not *interrupts-enabled*) *allow-with-interrupts*) ;; If interrupts are disabled, but we are allowed to ;; enabled them, check for pending interrupts every once - ;; in a while. - (loop - (loop repeat 128 do (cas)) ; 128 is arbitrary here - (sb!unix::%check-interrupts)) + ;; in a while. %CHECK-INTERRUPTS is taking shortcuts, make + ;; sure that deferrables are unblocked by doing an empty + ;; WITH-INTERRUPTS once. + (progn + (with-interrupts) + (loop + (loop repeat 128 do (cas)) ; 128 is arbitrary here + (sb!unix::%check-interrupts))) (loop (cas))))) t)) @@ -334,13 +342,16 @@ directly." +lock-taken+ +lock-contested+)))) ;; Wait on the contested lock. - (multiple-value-bind (to-sec to-usec) (decode-timeout nil) - (when (= 1 (with-pinned-objects (mutex) - (futex-wait (mutex-state-address mutex) - (get-lisp-obj-address +lock-contested+) - (or to-sec -1) - (or to-usec 0)))) - (signal-deadline)))) + (loop + (multiple-value-bind (to-sec to-usec) (decode-timeout nil) + (case (with-pinned-objects (mutex) + (futex-wait (mutex-state-address mutex) + (get-lisp-obj-address +lock-contested+) + (or to-sec -1) + (or to-usec 0))) + ((1) (signal-deadline)) + ((2)) + (otherwise (return)))))) (setf old (sb!ext:compare-and-swap (mutex-state mutex) +lock-free+ +lock-contested+)) @@ -464,16 +475,19 @@ time we reacquire MUTEX and return to the caller." ;; futex-wait returns immediately instead of sleeping. ;; Ergo, no lost wakeup. We may get spurious wakeups, but ;; that's ok. - (multiple-value-bind (to-sec to-usec) (decode-timeout nil) - (when (= 1 (with-pinned-objects (queue me) - (allow-with-interrupts - (futex-wait (waitqueue-data-address queue) - (get-lisp-obj-address me) - ;; our way if saying "no - ;; timeout": - (or to-sec -1) - (or to-usec 0))))) - (signal-deadline)))) + (loop + (multiple-value-bind (to-sec to-usec) (decode-timeout nil) + (case (with-pinned-objects (queue me) + (allow-with-interrupts + (futex-wait (waitqueue-data-address queue) + (get-lisp-obj-address me) + ;; our way if saying "no + ;; timeout": + (or to-sec -1) + (or to-usec 0)))) + ((1) (signal-deadline)) + ((2)) + (otherwise (return)))))) ;; 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 @@ -638,9 +652,6 @@ on this semaphore, then N of them is woken up." #!+sb-thread (defun handle-thread-exit (thread) (/show0 "HANDLING THREAD EXIT") - ;; We're going down, can't handle interrupts sanely anymore. GC - ;; remains enabled. - (block-deferrable-signals) ;; Lisp-side cleanup (with-all-threads-lock (setf (thread-%alive-p thread) nil) @@ -818,28 +829,50 @@ around and can be retrieved by JOIN-THREAD." (format nil "~~@" *current-thread*)) - (unwind-protect - (progn - ;; now that most things have a chance to - ;; work properly without messing up other - ;; threads, it's time to enable signals - (sb!unix::reset-signal-mask) - (setf (thread-result thread) - (cons t - (multiple-value-list - (funcall real-function))))) - (handle-thread-exit 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. - (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)))) + ;; 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)) @@ -859,13 +892,13 @@ around and can be retrieved by JOIN-THREAD." "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-mutex ((thread-result-lock thread)) + (with-system-mutex ((thread-result-lock thread) :allow-with-interrupts t) (cond ((car (thread-result thread)) - (values-list (cdr (thread-result thread)))) + (return-from join-thread + (values-list (cdr (thread-result thread))))) (defaultp - default) - (t - (error 'join-thread-error :thread thread))))) + (return-from join-thread default)))) + (error 'join-thread-error :thread thread)) (defun destroy-thread (thread) #!+sb-doc @@ -888,47 +921,54 @@ return DEFAULT if given or else signal JOIN-THREAD-ERROR." `(with-system-mutex ((thread-interruptions-lock ,thread)) ,@body)) -;;; Called from the signal handler in C. +;;; Called from the signal handler. +#!-win32 (defun run-interruption () - (in-interruption () - (loop - (let ((interruption (with-interruptions-lock (*current-thread*) - (pop (thread-interruptions *current-thread*))))) - (if interruption - (with-interrupts - (funcall interruption)) - (return)))))) - -;;; The order of interrupt execution is peculiar. If thread A -;;; interrupts thread B with I1, I2 and B for some reason receives I1 -;;; when FUN2 is already on the list, then it is FUN2 that gets to run -;;; first. But when FUN2 is run SIG_INTERRUPT_THREAD is enabled again -;;; and I2 hits pretty soon in FUN2 and run FUN1. This is of course -;;; just one scenario, and the order of thread interrupt execution is -;;; undefined. + (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." - #!-sb-thread (declare (ignore thread)) - #!-sb-thread +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))) - #!+sb-thread - (if (eq thread *current-thread*) - (with-interrupt-bindings - (with-interrupts (funcall function))) - (let ((os-thread (thread-os-thread thread))) - (cond ((not os-thread) - (error 'interrupt-thread-error :thread thread)) - (t - (with-interruptions-lock (thread) - (push function (thread-interruptions thread))) - (when (minusp (signal-interrupt-thread os-thread)) - (error 'interrupt-thread-error :thread thread))))))) + #!-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