0.8.6.10
authorDaniel Barlow <dan@telent.net>
Fri, 28 Nov 2003 04:41:05 +0000 (04:41 +0000)
committerDaniel Barlow <dan@telent.net>
Fri, 28 Nov 2003 04:41:05 +0000 (04:41 +0000)
The diff is kind of noisy, but it's really pretty simple

The goal here is to deal with multiple 'session's in a
slightly more sendible way, and for QUIT to kill off
background threads if any are running

We create a SESSION struct with various locks and queues,
so replacing about 4 special variables with one *SESSION*
structure object.

MAKE-THREAD adds the new thread id to the current session

WITH-NEW-SESSION removes the current thread from the session,
then creates a new session containing only the current thread
and rebinds *SESSION* to it.  It's used by MAKE-LISTENER-THREAD
and is also exported albeit in a "we reserve the right to
change this but are unlikely to" fashion

TERMINATE-SESSION terminates all threads in the session other
than the caller.  %END-OF-THE-WORLD-HANDLER calls it

Also exported *INVOKE-DEBUGGER-HOOK* from SB-EXT for SLIME and
similar alternate debugging interfaces to use.

package-data-list.lisp-expr
src/code/save.lisp
src/code/target-thread.lisp
src/code/target-unithread.lisp
src/code/toplevel.lisp
version.lisp-expr

index ec6ed72..0ff5146 100644 (file)
@@ -630,6 +630,11 @@ like *STACK-TOP-HINT* and unsupported stuff like *TRACED-FUN-LIST*."
             "DISABLE-DEBUGGER"
             "ENABLE-DEBUGGER"
 
+            ;; the mechanism by which {en,dis}able-debugger works is
+            ;; also exported for people writing alternative toplevels
+            ;; (Emacs, CLIM interfaces, etc)
+            "*INVOKE-DEBUGGER-HOOK*"
+
              ;; miscellaneous useful supported extensions
              "QUIT"
             "*MODULE-PROVIDER-FUNCTIONS*"
@@ -1472,7 +1477,8 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
             "MUTEX" "MAKE-MUTEX" "GET-MUTEX" "RELEASE-MUTEX" "WITH-MUTEX"
             "MUTEX-VALUE" "WAITQUEUE" "MAKE-WAITQUEUE"
             "CONDITION-WAIT" "CONDITION-NOTIFY" "CONDITION-BROADCAST"
-            "WITH-RECURSIVE-LOCK" "RELEASE-FOREGROUND" "CURRENT-THREAD-ID"))
+            "WITH-RECURSIVE-LOCK" "RELEASE-FOREGROUND" "WITH-NEW-SESSION"
+            "CURRENT-THREAD-ID"))
  
  #s(sb-cold:package-data
     :name "SB!LOOP"
index f1ed1e9..76b7b95 100644 (file)
@@ -38,6 +38,7 @@
   This implementation is not as polished and painless as you might like: 
     * It corrupts the current Lisp image enough that the current process
       needs to be killed afterwards.
+    * It will not work if multiple threads are in use.
     * There is absolutely no binary compatibility of core images between
       different runtime support programs. Even runtimes built from the same
       sources at different times are treated as incompatible for this purpose.
@@ -74,8 +75,6 @@
       This is reinitialized to reflect the working directory where the
       saved core is loaded."
 
-  ;; FIXME (sb!mp::shutdown-multi-processing)
-  ;; FIXME: What is this for? Explain.
   (when (fboundp 'cancel-finalization)
     (cancel-finalization sb!sys:*tty*))
   ;; FIXME: Would it be possible to unmix the PURIFY logic from this
index 9b69d91..59e96ad 100644 (file)
     sb!alien:unsigned-int
   (thread-pid sb!alien:unsigned-long))
 
+(defvar *session* nil)
 
 (defun make-thread (function)
-  (let ((real-function (coerce function 'function)))
-    (%create-thread
-     (sb!kernel:get-lisp-obj-address
-      (lambda ()
-       ;; in time we'll move some of the binding presently done in C
-       ;; here too
-       (let ((sb!kernel::*restart-clusters* nil)
-             (sb!impl::*descriptor-handlers* nil); serve-event
-             (sb!impl::*available-buffers* nil)) ;for fd-stream
-         ;; 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)
-         (sb!sys:enable-interrupt sb!unix:sigint :ignore)
-         (sb!unix:unix-exit
-          (catch 'sb!impl::%end-of-the-world 
-            (with-simple-restart 
-                (destroy-thread
-                 (format nil "~~@<Destroy this thread (~A)~~@:>"
-                         (current-thread-id)))
-              (funcall real-function))
-            0))))))))
+  (let* ((real-function (coerce function 'function))
+        (tid
+         (%create-thread
+          (sb!kernel:get-lisp-obj-address
+           (lambda ()
+             ;; in time we'll move some of the binding presently done in C
+             ;; here too
+             (let ((sb!kernel::*restart-clusters* nil)
+                   (sb!impl::*descriptor-handlers* nil) ; serve-event
+                   (sb!impl::*available-buffers* nil)) ;for fd-stream
+               ;; 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)
+               (sb!sys:enable-interrupt sb!unix:sigint :ignore)
+               (sb!unix:unix-exit
+                (catch 'sb!impl::%end-of-the-world 
+                  (with-simple-restart 
+                      (destroy-thread
+                       (format nil "~~@<Destroy this thread (~A)~~@:>"
+                               (current-thread-id)))
+                    (funcall real-function))
+                  0))))))))
+    (with-mutex ((session-lock *session*))
+      (pushnew tid (session-threads *session*)))
+    tid))
 
 ;;; Really, you don't want to use these: they'll get into trouble with
 ;;; garbage collection.  Use a lock or a waitqueue instead
@@ -320,86 +325,114 @@ time we reacquire LOCK and return to the caller."
          (fdefinition 'condition-notify) #'condition-notify/futex)
     t))
 
-;;;; multiple independent listeners
+;;;; job control, independent listeners
 
-(defvar *session-lock* nil)
+(defstruct session 
+  (lock (make-mutex))
+  (threads nil)
+  (interactive-threads nil)
+  (interactive-threads-queue (make-waitqueue)))
 
-(defun make-listener-thread (tty-name)  
-  (assert (probe-file tty-name))
-  ;; FIXME probably still need to do some tty stuff to get signals
-  ;; delivered correctly.
-  ;; FIXME 
-  (let* ((in (sb!unix:unix-open (namestring tty-name) sb!unix:o_rdwr #o666))
-        (out (sb!unix:unix-dup in))
-        (err (sb!unix:unix-dup in)))
-    (labels ((thread-repl () 
-              (sb!unix::unix-setsid)
-              (let* ((*session-lock*
-                      (make-mutex :name (format nil "lock for ~A" tty-name)))
-                     (sb!impl::*stdin* 
-                      (sb!sys:make-fd-stream in :input t :buffering :line))
-                     (sb!impl::*stdout* 
-                      (sb!sys:make-fd-stream out :output t :buffering :line))
-                     (sb!impl::*stderr* 
-                      (sb!sys:make-fd-stream err :output t :buffering :line))
-                     (sb!impl::*tty* 
-                      (sb!sys:make-fd-stream err :input t :output t :buffering :line))
-                     (sb!impl::*descriptor-handlers* nil))
-                (get-mutex *session-lock*)
-                (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
-                (unwind-protect
-                     (sb!impl::toplevel-repl nil)
-                  (sb!int:flush-standard-output-streams)))))
-      (make-thread #'thread-repl))))
-  
-;;;; job control
-
-
-(defvar *interactive-threads-lock* 
-  (make-mutex :name "*interactive-threads* lock"))
-(defvar *interactive-threads* nil)
-(defvar *interactive-threads-queue*
-  (make-waitqueue :name "All threads that need the terminal.  First ID on this list is running, the others are waiting"))
+(defun new-session ()
+  (let ((tid (current-thread-id)))
+    (make-session :threads (list tid)
+                 :interactive-threads (list tid))))
 
 (defun init-job-control ()
-  (with-mutex (*interactive-threads-lock*)
-    (setf *interactive-threads* (list (current-thread-id)))
-    (return-from init-job-control t)))
+  (setf *session* (new-session)))
+
+(defun call-with-new-session (fn)
+  (let ((tid (current-thread-id)))
+    (with-mutex ((session-lock *session*))
+      (setf (session-threads *session*)
+           (delete tid (session-threads *session*))
+           (session-interactive-threads *session*)
+           (delete tid (session-interactive-threads *session*))))
+    (let ((*session* (new-session)))
+      (funcall fn))))
+
+(defmacro with-new-session (args &body forms)
+  (declare (ignore args))              ;for extensibility
+  (sb!int:with-unique-names (fb-name)
+    `(labels ((,fb-name () ,@forms))
+      (call-with-new-session (function ,fb-name)))))
+
+(defun terminate-session ()
+  "Kill all threads in session exept for this one.  Does nothing if current
+thread is not the foreground thread"
+  (let* ((tid (current-thread-id))
+        (to-kill
+         (with-mutex ((session-lock *session*))
+           (and (eql tid (car (session-interactive-threads *session*)))
+                (session-threads *session*)))))
+    ;; do the kill after dropping the mutex; unwind forms in dying
+    ;; threads may want to do session things
+    (dolist (p to-kill)
+      (unless (eql p tid) (terminate-thread p)))))
 
 ;;; called from top of invoke-debugger
 (defun debugger-wait-until-foreground-thread (stream)
   "Returns T if thread had been running in background, NIL if it was
 interactive."
   (prog1
-      (with-mutex (*interactive-threads-lock*)
-       (not (member (current-thread-id) *interactive-threads*)))
+      (with-mutex ((session-lock *session*))
+       (not (member (current-thread-id) 
+                    (session-interactive-threads *session*))))
     (get-foreground)))
 
 (defun thread-repl-prompt-fun (out-stream)
   (get-foreground)
-  (let ((stopped-threads (cdr *interactive-threads*)))
+  (let ((stopped-threads (cdr (session-interactive-threads *session*))))
     (when stopped-threads
       (format out-stream "~{~&Thread ~A suspended~}~%" stopped-threads))
     (sb!impl::repl-prompt-fun out-stream)))
 
 (defun get-foreground ()
   (loop
-   (with-mutex (*interactive-threads-lock*)
+   (with-mutex ((session-lock *session*))
      (let ((tid (current-thread-id)))
-       (when (eql (car *interactive-threads*) tid)
+       (when (eql (car (session-interactive-threads *session*)) tid)
         (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
         (return-from get-foreground t))
        (unless (member tid *interactive-threads*)
-        (setf (cdr (last *interactive-threads*)) (list tid)))
+        (setf (cdr (last (session-interactive-threads *session*)))
+              (list tid)))
        (condition-wait
-       *interactive-threads-queue* *interactive-threads-lock* )))))
+       (session-interactive-threads-queue *session*)
+       (session-lock *session*))))))
 
 (defun release-foreground (&optional next)
   "Background this thread.  If NEXT is supplied, arrange for it to have the foreground next"
-  (with-mutex (*interactive-threads-lock*)
+  (with-mutex ((session-lock *session*))
     (let ((tid (current-thread-id)))
-      (setf *interactive-threads* (delete tid *interactive-threads*))
+      (setf (session-interactive-threads *session*)
+           (delete tid *interactive-threads*))
       (sb!sys:enable-interrupt sb!unix:sigint :ignore)
-      (when next (setf *interactive-threads*
-                      (list* next (delete next *interactive-threads*))))
-      (condition-broadcast *interactive-threads-queue*))))
\ No newline at end of file
+      (when next 
+       (setf (session-interactive-threads *session*)
+             (list* next 
+                    (delete next (session-interactive-threads *session*)))))
+      (condition-broadcast (session-interactive-threads-queue *session*)))))
+
+(defun make-listener-thread (tty-name)  
+  (assert (probe-file tty-name))
+  (let* ((in (sb!unix:unix-open (namestring tty-name) sb!unix:o_rdwr #o666))
+        (out (sb!unix:unix-dup in))
+        (err (sb!unix:unix-dup in)))
+    (labels ((thread-repl () 
+              (sb!unix::unix-setsid)
+              (let* ((sb!impl::*stdin* 
+                      (sb!sys:make-fd-stream in :input t :buffering :line))
+                     (sb!impl::*stdout* 
+                      (sb!sys:make-fd-stream out :output t :buffering :line))
+                     (sb!impl::*stderr* 
+                      (sb!sys:make-fd-stream err :output t :buffering :line))
+                     (sb!impl::*tty* 
+                      (sb!sys:make-fd-stream err :input t :output t :buffering :line))
+                     (sb!impl::*descriptor-handlers* nil))
+                (with-new-session ()
+                  (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
+                  (unwind-protect
+                       (sb!impl::toplevel-repl nil)
+                    (sb!int:flush-standard-output-streams))))))
+      (make-thread #'thread-repl))))
index b8ae72f..651573d 100644 (file)
        (t 
         `(progn ,@body))))
 
-;;; what's the best thing to do with these on unithread?
+;;; what's the best thing to do with these on unithread?  commented
+;;; functions are the thread versions, just to remind me what they do
+;;; there
 #+nil
 (defun condition-wait (queue lock)
   "Atomically release LOCK and enqueue ourselves on QUEUE.  Another
@@ -129,3 +131,4 @@ time we reacquire LOCK and return to the caller."
 (defun debugger-wait-until-foreground-thread (stream) t)
 (defun get-foreground () t)
 (defun release-foreground (&optional next) t)
+(defun terminate-session ())
index 91bd1cb..03f4c34 100644 (file)
     `(let ((,caught (catch '%end-of-the-world
                      (/show0 "inside CATCH '%END-OF-THE-WORLD")
                      ,@body)))
-       (/show0 "back from CATCH '%END-OF-THE-WORLD, flushing output")
-       (flush-standard-output-streams)
-       (/show0 "calling UNIX-EXIT")
-       (sb!unix:unix-exit ,caught))))
+      (/show0 "back from CATCH '%END-OF-THE-WORLD, flushing output")
+      (flush-standard-output-streams)
+      (sb!thread::terminate-session)
+      (/show0 "calling UNIX-EXIT")
+      (sb!unix:unix-exit ,caught))))
 \f
 ;;;; working with *CURRENT-ERROR-DEPTH* and *MAXIMUM-ERROR-DEPTH*
 
index 1ea7adf..a0497e9 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".)
-"0.8.6.8"
+"0.8.6.9"