1.0.3.45: added JOIN-THREAD
authorGabor Melis <mega@hotpop.com>
Sun, 18 Mar 2007 19:30:25 +0000 (19:30 +0000)
committerGabor Melis <mega@hotpop.com>
Sun, 18 Mar 2007 19:30:25 +0000 (19:30 +0000)
Implementation by NIIMI Satoshi. Added more docstrings and changed the
interface according to the styling advice of Thomas F. Burdick.

NEWS
doc/manual/threading.texinfo
package-data-list.lisp-expr
src/code/target-thread.lisp
src/code/timer.lisp
tests/threads.impure.lisp
version.lisp-expr

diff --git a/NEWS b/NEWS
index 2c64fc9..04d4735 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ changes in sbcl-1.0.4 relative to sbcl-1.0.3:
   * change: runtimes with embedded cores (i.e. saved with :EXECUTABLE T)
     don't print the startup banner, but behave as if --noinform was passed
     as a command line argument. (thanks to Kevin Reid)
+  * new feature: added JOIN-THREAD (by NIIMI Satoshi)
   * optimization: code using alien values with undeclared types is much faster.
   * optimization: the compiler is now able to open code SEARCH in more cases.
   * optimization: more compact typechecks on x86-64 (thanks to Lutz Euler)
index 7462466..759ab0f 100644 (file)
@@ -33,6 +33,9 @@ threading on Darwin (Mac OS X) and FreeBSD on the x86 is experimental.
 @include struct-sb-thread-thread.texinfo
 @include var-sb-thread-star-current-thread-star.texinfo
 @include fun-sb-thread-make-thread.texinfo
+@include fun-sb-thread-join-thread.texinfo
+@include condition-sb-thread-join-thread-error.texinfo
+@include fun-sb-thread-join-thread-error-thread.texinfo
 @include fun-sb-thread-thread-alive-p.texinfo
 @include fun-sb-thread-list-all-threads.texinfo
 @include condition-sb-thread-interrupt-thread-error.texinfo
index 89f0d84..fe1ab00 100644 (file)
@@ -1642,6 +1642,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
       :export ("*CURRENT-THREAD*" "THREAD" "MAKE-THREAD"
                "THREAD-NAME" "THREAD-ALIVE-P"
                "LIST-ALL-THREADS"
+               "JOIN-THREAD" "JOIN-THREAD-ERROR" "JOIN-THREAD-ERROR-THREAD"
                "INTERRUPT-THREAD-ERROR"
                "INTERRUPT-THREAD-ERROR-THREAD"
                "INTERRUPT-THREAD" "TERMINATE-THREAD" "DESTROY-THREAD"
index 06002df..5d1009b 100644 (file)
@@ -30,7 +30,9 @@ in future versions."
   %alive-p
   os-thread
   interruptions
-  (interruptions-lock (make-mutex :name "thread interruptions lock")))
+  (interruptions-lock (make-mutex :name "thread interruptions lock"))
+  result
+  (result-lock (make-mutex :name "thread result lock")))
 
 #!+sb-doc
 (setf (sb!kernel:fdocumentation 'thread-name 'function)
@@ -604,7 +606,8 @@ have the foreground next."
 (defun make-thread (function &key name)
   #!+sb-doc
   "Create a new thread of NAME that runs FUNCTION. When the function
-returns the thread exits."
+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
@@ -638,30 +641,34 @@ returns the thread exits."
                   (sb!impl::*internal-symbol-output-fun* nil)
                   (sb!impl::*descriptor-handlers* nil)) ; serve-event
               (setf (thread-os-thread thread) (current-thread-sap-id))
-              (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*))
-                    (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)
-                           (funcall real-function))
-                      (handle-thread-exit 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*))
+                      (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)))))))
             (values))))
     ;; Keep INITIAL-FUNCTION pinned until the child thread is
     ;; initialized properly.
@@ -674,6 +681,32 @@ returns the thread exits."
         (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 (sb!kernel:fdocumentation 'join-thread-error-thread 'function)
+      "The thread that we failed to join.")
+
+(defun join-thread (thread &key (errorp t) default)
+  #!+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 or signal JOIN-THREAD-ERROR depending on ERRORP."
+  (with-mutex ((thread-result-lock thread))
+    (cond ((car (thread-result thread))
+           (values-list (cdr (thread-result thread))))
+          (errorp
+           (error 'join-thread-error :thread thread))
+          (t
+           default))))
+
 (defun destroy-thread (thread)
   #!+sb-doc
   "Deprecated. Same as TERMINATE-THREAD."
index 3ad04ea..2d4c67a 100644 (file)
@@ -338,7 +338,8 @@ triggers."
                (sb!thread:interrupt-thread thread function)
              (sb!thread:interrupt-thread-error (c)
                (declare (ignore c))
-               (warn "Timer ~S failed to interrupt thread ~S." timer thread)))))))
+               (warn "Timer ~S failed to interrupt thread ~S."
+                     timer thread)))))))
 
 ;; Called from the signal handler.
 (defun run-expired-timers ()
index 0d5453b..97f98f4 100644 (file)
 (in-package "SB-THREAD") ; this is white-box testing, really
 
 (use-package :test-util)
+(use-package "ASSERTOID")
 
 (defun wait-for-threads (threads)
-  (loop while (some #'sb-thread:thread-alive-p threads) do (sleep 0.01)))
+  (mapc #'sb-thread:join-thread threads)
+  (assert (not (some #'sb-thread:thread-alive-p threads))))
 
 (assert (eql 1 (length (list-all-threads))))
 
   (sleep 3)
   (assert (not (thread-alive-p thread))))
 
+(with-test (:name '(:join-thread :nlx :default))
+  (let ((sym (gensym)))
+    (assert (eq sym (join-thread (make-thread (lambda () (sb-ext:quit)))
+                                 :default sym)))))
+
+(with-test (:name '(:join-thread :nlx :error))
+  (raises-error? (join-thread (make-thread (lambda () (sb-ext:quit)))
+                              :errorp t)))
+
+(with-test (:name '(:join-thread :multiple-values))
+  (assert (equal '(1 2 3)
+                 (multiple-value-list
+                  (join-thread (make-thread (lambda () (values 1 2 3))))))))
+
 ;;; We had appalling scaling properties for a while.  Make sure they
 ;;; don't reappear.
 (defun scaling-test (function &optional (nthreads 5))
   (let* ((ok t)
          (threads (loop for i from 0 to 10
                         collect (sb-thread:make-thread
-                                 (let ((i i))
-                                   (lambda ()
-                                     (dotimes (j 100)
-                                       (write-char #\-)
-                                       (finish-output)
-                                       (let ((n (infodb-test)))
-                                         (unless (zerop n)
-                                           (setf ok nil)
-                                           (format t "N != 0 (~A)~%" n)
-                                           (quit))))))))))
+                                 (lambda ()
+                                   (dotimes (j 100)
+                                     (write-char #\-)
+                                     (finish-output)
+                                     (let ((n (infodb-test)))
+                                       (unless (zerop n)
+                                         (setf ok nil)
+                                         (format t "N != 0 (~A)~%" n)
+                                         (sb-ext:quit)))))))))
     (wait-for-threads threads)
     (assert ok)))
 
index c6340dd..60c34a1 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.3.44"
+"1.0.3.45"