0.8.6.5
authorDaniel Barlow <dan@telent.net>
Thu, 27 Nov 2003 06:21:04 +0000 (06:21 +0000)
committerDaniel Barlow <dan@telent.net>
Thu, 27 Nov 2003 06:21:04 +0000 (06:21 +0000)
"Well, the hours are pretty good"

Merged the resistance-is-futex branch: see commit messages on
branch for scary details

"... but now I come to think about it, most of the actual minutes
are pretty lousy"

21 files changed:
CREDITS
base-target-features.lisp-expr
contrib/sb-posix/README
package-data-list.lisp-expr
src/code/cold-init.lisp
src/code/cross-thread.lisp
src/code/gc.lisp
src/code/target-thread.lisp
src/code/target-unithread.lisp
src/code/thread.lisp
src/code/toplevel.lisp
src/runtime/gencgc.c
src/runtime/interrupt.c
src/runtime/interrupt.h
src/runtime/linux-os.c
src/runtime/linux-os.h
src/runtime/thread.c
src/runtime/x86-arch.c
tests/threads.impure.lisp
tools-for-build/grovel-headers.c
version.lisp-expr

diff --git a/CREDITS b/CREDITS
index 4968cbb..d2a45a1 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -512,7 +512,7 @@ Daniel Barlow:
   and PPC ports (from CMUCL), control stack exhaustion checking (new)
   and native threads support for x86 Linux (new).  He also refactored
   the garbage collectors for understandability, wrote code
   and PPC ports (from CMUCL), control stack exhaustion checking (new)
   and native threads support for x86 Linux (new).  He also refactored
   the garbage collectors for understandability, wrote code
-  (e.g. grovel_headers.c and stat_wrapper stuff) to find
+  (e.g. grovel-headers.c and stat_wrapper stuff) to find
   machine-dependent and OS-dependent constants automatically, and was
   original author of the asdf, asdf-install, sb-bsd-sockets,
   sb-executable, sb-grovel and sb-posix contrib packages.
   machine-dependent and OS-dependent constants automatically, and was
   original author of the asdf, asdf-install, sb-bsd-sockets,
   sb-executable, sb-grovel and sb-posix contrib packages.
index 6d10856..5b97f72 100644 (file)
  ;; Note that no consistent effort to audit the SBCL library code for
  ;; thread safety has been performed, so caveat executor.
  ; :sb-thread
  ;; Note that no consistent effort to audit the SBCL library code for
  ;; thread safety has been performed, so caveat executor.
  ; :sb-thread
+
+ ;; Kernel support for futexes (so-called "fast userspace mutexes") is
+ ;; available in Linux 2.6 and some versions of 2.4 (Red Hat vendor
+ ;; kernels, possibly other vendors too).  We can take advantage of
+ ;; these to do faster and probably more reliable mutex and condition
+ ;; variable support.  An SBCL built with this feature will fall back
+ ;; to the old system if the futex() syscall is not available at
+ ;; runtime
+ ; :sb-futex
  
  ;; This affects the definition of a lot of things in bignum.lisp. It
  ;; doesn't seem to be documented anywhere what systems it might apply
  
  ;; This affects the definition of a lot of things in bignum.lisp. It
  ;; doesn't seem to be documented anywhere what systems it might apply
index e62ea00..832eea1 100644 (file)
@@ -80,9 +80,9 @@ results if the stream is buffered.
 
 A filename is a string.  
 
 
 A filename is a string.  
 
-A pathname is a designator for a file-descriptor: the filename is
-computed using the same mechanism as the implementation would
-use to map pathnames to OS filenames internally.
+A pathname is a designator for a filename: the filename is computed
+using the same mechanism as the implementation would use to map
+pathnames to OS filenames internally.
 
 In an implementation that supports pathnames to files on other hosts, 
 using mechanisms not available to the underlying OS (for example, 
 
 In an implementation that supports pathnames to files on other hosts, 
 using mechanisms not available to the underlying OS (for example, 
@@ -166,11 +166,12 @@ is obvious.  For example,
 (read fd buffer &optional (length (length buffer))) => bytes-read
 
 b) where C simulates "out" parameters using pointers (for instance, in
 (read fd buffer &optional (length (length buffer))) => bytes-read
 
 b) where C simulates "out" parameters using pointers (for instance, in
-pipe() or socketpair()) we may use multiple return values instead.
-This doesn't apply to data transfer functions that fill buffers.
+pipe() or socketpair()) these may be optional or omitted in the Lisp
+interface: if not provided, appropriate objects will be allocated and
+returned (using multiple return values if necessary).
 
 c) some functions accept objects such as filenames or file
 
 c) some functions accept objects such as filenames or file
-descriptors.  Wherver these are specified as such in the C bindings,
+descriptors.  Wherever these are specified as such in the C bindings,
 the Lisp interface accepts designators for them as specified in the
 'Types' section above
 
 the Lisp interface accepts designators for them as specified in the
 'Types' section above
 
index 5adc2ea..ec6ed72 100644 (file)
@@ -1470,7 +1470,8 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
             "MAKE-LISTENER-THREAD" "DESTROY-THREAD" "TERMINATE-THREAD"
             "INTERRUPT-THREAD" "WITH-RECURSIVE-LOCK"
             "MUTEX" "MAKE-MUTEX" "GET-MUTEX" "RELEASE-MUTEX" "WITH-MUTEX"
             "MAKE-LISTENER-THREAD" "DESTROY-THREAD" "TERMINATE-THREAD"
             "INTERRUPT-THREAD" "WITH-RECURSIVE-LOCK"
             "MUTEX" "MAKE-MUTEX" "GET-MUTEX" "RELEASE-MUTEX" "WITH-MUTEX"
-            "WAITQUEUE" "MAKE-WAITQUEUE" "CONDITION-WAIT" "CONDITION-NOTIFY"
+            "MUTEX-VALUE" "WAITQUEUE" "MAKE-WAITQUEUE"
+            "CONDITION-WAIT" "CONDITION-NOTIFY" "CONDITION-BROADCAST"
             "WITH-RECURSIVE-LOCK" "RELEASE-FOREGROUND" "CURRENT-THREAD-ID"))
  
  #s(sb-cold:package-data
             "WITH-RECURSIVE-LOCK" "RELEASE-FOREGROUND" "CURRENT-THREAD-ID"))
  
  #s(sb-cold:package-data
index bc7d9f5..bc37148 100644 (file)
@@ -289,6 +289,7 @@ instead (which is another name for the same thing)."))
       ;; disabled by default. Joe User can explicitly enable them if
       ;; desired.
       (set-floating-point-modes :traps '(:overflow :invalid :divide-by-zero))
       ;; disabled by default. Joe User can explicitly enable them if
       ;; desired.
       (set-floating-point-modes :traps '(:overflow :invalid :divide-by-zero))
+      (sb!thread::maybe-install-futex-functions)
 
       ;; Clear pseudo atomic in case this core wasn't compiled with
       ;; support.
 
       ;; Clear pseudo atomic in case this core wasn't compiled with
       ;; support.
index eafb3fb..eb71d1f 100644 (file)
@@ -5,3 +5,5 @@
 (defmacro with-recursive-lock ((mutex) &body body)
   `(progn ,@body))
 
 (defmacro with-recursive-lock ((mutex) &body body)
   `(progn ,@body))
 
+
+
index eabb4b4..10f4bce 100644 (file)
@@ -230,28 +230,27 @@ and submit it as a patch."
 
 ;;; For GENCGC all generations < GEN will be GC'ed.
 
 
 ;;; For GENCGC all generations < GEN will be GC'ed.
 
-(defvar *already-in-gc* nil "System is running SUB-GC")
-(defvar *gc-mutex* (sb!thread:make-mutex :name "GC Mutex"))
+(defvar *already-in-gc* 
+  (sb!thread:make-mutex :name "GC lock") "ID of thread running SUB-GC")
 
 (defun sub-gc (&key (gen 0) &aux (pre-gc-dynamic-usage (dynamic-usage)))
 
 (defun sub-gc (&key (gen 0) &aux (pre-gc-dynamic-usage (dynamic-usage)))
-  ;; catch attempts to gc recursively or during post-hooks and ignore them
-  (when (sb!thread::mutex-value *gc-mutex*)  (return-from sub-gc nil))
-  (sb!thread:with-mutex (*gc-mutex* :wait-p nil)
+  (let ((me (sb!thread:current-thread-id)))
+    (when (eql (sb!thread::mutex-value *already-in-gc*) me) 
+      (return-from sub-gc nil))
     (setf *need-to-collect-garbage* t)
     (when (zerop *gc-inhibit*)
     (setf *need-to-collect-garbage* t)
     (when (zerop *gc-inhibit*)
-      (without-interrupts
-       (gc-stop-the-world)
-       (collect-garbage gen)
-       (incf *n-bytes-freed-or-purified*
-            (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
-       (setf *need-to-collect-garbage* nil)
-       (gc-start-the-world))
-      (scrub-control-stack)
-      (setf *need-to-collect-garbage* nil)
-      (dolist (h *after-gc-hooks*) (carefully-funcall h))))
-  (values))
-       
-
+      (loop
+       (sb!thread:with-mutex (*already-in-gc*)
+        (unless *need-to-collect-garbage* (return-from sub-gc nil))
+        (without-interrupts
+         (gc-stop-the-world)
+         (collect-garbage gen)
+         (incf *n-bytes-freed-or-purified*
+               (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
+         (scrub-control-stack)
+         (setf *need-to-collect-garbage* nil)
+         (dolist (h *after-gc-hooks*) (carefully-funcall h))
+         (gc-start-the-world)))))))
 
 ;;; This is the user-advertised garbage collection function.
 (defun gc (&key (gen 0) (full nil) &allow-other-keys)
 
 ;;; This is the user-advertised garbage collection function.
 (defun gc (&key (gen 0) (full nil) &allow-other-keys)
index 2601a46..9b69d91 100644 (file)
@@ -1,9 +1,18 @@
 (in-package "SB!THREAD")
 
 (in-package "SB!THREAD")
 
+;;; 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
+
 (sb!alien::define-alien-routine ("create_thread" %create-thread)
      sb!alien:unsigned-long
   (lisp-fun-address sb!alien:unsigned-long))
 
 (sb!alien::define-alien-routine ("create_thread" %create-thread)
      sb!alien:unsigned-long
   (lisp-fun-address sb!alien:unsigned-long))
 
+(sb!alien::define-alien-routine "signal_thread_to_dequeue"
+    sb!alien:unsigned-int
+  (thread-pid sb!alien:unsigned-long))
+
+
 (defun make-thread (function)
   (let ((real-function (coerce function 'function)))
     (%create-thread
 (defun make-thread (function)
   (let ((real-function (coerce function 'function)))
     (%create-thread
@@ -106,17 +115,43 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
 
 ;;;; the higher-level locking operations are based on waitqueues
 
 
 ;;;; the higher-level locking operations are based on waitqueues
 
+(declaim (inline waitqueue-data-address mutex-value-address))
+
 (defstruct waitqueue
   (name nil :type (or null simple-base-string))
   (lock 0)
   (data nil))
 
 (defstruct waitqueue
   (name nil :type (or null simple-base-string))
   (lock 0)
   (data nil))
 
+;;; The bare 4 here and 5 below are offsets of the slots in the struct.
+;;; There ought to be some better way to get these numbers
+(defun waitqueue-data-address (lock)
+  (declare (optimize (speed 3)))
+  (sb!ext:truly-the
+   (unsigned-byte 32)
+   (+ (sb!kernel:get-lisp-obj-address lock)
+      (- (* 4 sb!vm:n-word-bytes) sb!vm:instance-pointer-lowtag))))
+
 (defstruct (mutex (:include waitqueue))
   (value nil))
 
 (defstruct (mutex (:include waitqueue))
   (value nil))
 
+(defun mutex-value-address (lock)
+  (declare (optimize (speed 3)))
+  (sb!ext:truly-the
+   (unsigned-byte 32)
+   (+ (sb!kernel:get-lisp-obj-address lock)
+      (- (* 5 sb!vm:n-word-bytes) sb!vm:instance-pointer-lowtag))))
+
 (sb!alien:define-alien-routine "block_sigcont"  void)
 (sb!alien:define-alien-routine "unblock_sigcont_and_sleep"  void)
 
 (sb!alien:define-alien-routine "block_sigcont"  void)
 (sb!alien:define-alien-routine "unblock_sigcont_and_sleep"  void)
 
+#!+sb-futex
+(declaim (inline futex-wait futex-wake))
+#!+sb-futex
+(sb!alien:define-alien-routine
+    "futex_wait" int (word unsigned-long) (old-value unsigned-long))
+#!+sb-futex
+(sb!alien:define-alien-routine
+    "futex_wake" int (word unsigned-long) (n unsigned-long))
 
 ;;; this should only be called while holding the queue spinlock.
 ;;; it releases the spinlock before sleeping
 
 ;;; this should only be called while holding the queue spinlock.
 ;;; it releases the spinlock before sleeping
@@ -140,13 +175,14 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
 ;;; this should only be called while holding the queue spinlock.
 (defun signal-queue-head (queue)
   (let ((p (car (waitqueue-data queue))))
 ;;; this should only be called while holding the queue spinlock.
 (defun signal-queue-head (queue)
   (let ((p (car (waitqueue-data queue))))
-    (when p (sb!unix:unix-kill p  sb!unix::sig-dequeue))))
+    (when p (signal-thread-to-dequeue p))))
 
 ;;;; mutex
 
 
 ;;;; mutex
 
+;;; i suspect there may be a race still in this: the futex version requires
+;;; the old mutex value before sleeping, so how do we get away without it
 (defun get-mutex (lock &optional new-value (wait-p t))
 (defun get-mutex (lock &optional new-value (wait-p t))
-  (declare (type mutex lock)
-          (optimize (speed 3)))
+  (declare (type mutex lock) (optimize (speed 3)))
   (let ((pid (current-thread-id)))
     (unless new-value (setf new-value pid))
     (assert (not (eql new-value (mutex-value lock))))
   (let ((pid (current-thread-id)))
     (unless new-value (setf new-value pid))
     (assert (not (eql new-value (mutex-value lock))))
@@ -163,6 +199,21 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
        (return nil))
      (wait-on-queue lock nil))))
 
        (return nil))
      (wait-on-queue lock nil))))
 
+#!+sb-futex
+(defun get-mutex/futex (lock &optional new-value (wait-p t))
+  (declare (type mutex lock)  (optimize (speed 3)))
+  (let ((pid (current-thread-id))
+       old)
+    (unless new-value (setf new-value pid))
+    (assert (not (eql new-value (mutex-value lock))))
+    (loop
+     (unless
+        (setf old (sb!vm::%instance-set-conditional lock 4 nil new-value))
+       (return t))
+     (unless wait-p (return nil))
+     (futex-wait (mutex-value-address lock)
+                (sb!kernel:get-lisp-obj-address old)))))
+
 (defun release-mutex (lock &optional (new-value nil))
   (declare (type mutex lock))
   ;; we assume the lock is ours to release
 (defun release-mutex (lock &optional (new-value nil))
   (declare (type mutex lock))
   ;; we assume the lock is ours to release
@@ -170,6 +221,12 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
     (setf (mutex-value lock) new-value)
     (signal-queue-head lock)))
 
     (setf (mutex-value lock) new-value)
     (signal-queue-head lock)))
 
+#!+sb-futex
+(defun release-mutex/futex (lock)
+  (declare (type mutex lock))
+  (setf (mutex-value lock) nil)
+  (futex-wake (mutex-value-address lock) 1))
+
 
 (defmacro with-mutex ((mutex &key value (wait-p t))  &body body)
   (with-unique-names (got)
 
 (defmacro with-mutex ((mutex &key value (wait-p t))  &body body)
   (with-unique-names (got)
@@ -200,10 +257,68 @@ time we reacquire LOCK and return to the caller."
        (dequeue queue))
       (get-mutex lock value))))
 
        (dequeue queue))
       (get-mutex lock value))))
 
+#!+sb-futex
+(defun condition-wait/futex (queue lock)
+  (assert lock)
+  (let ((value (mutex-value lock)))
+    (unwind-protect
+        (let ((me (current-thread-id)))
+          ;; XXX we should do something to ensure that the result of this setf
+          ;; is visible to all CPUs
+          (setf (waitqueue-data queue) me)
+          (release-mutex lock)
+          ;; Now we go to sleep using futex-wait.  If anyone else
+          ;; manages to grab LOCK and call CONDITION-NOTIFY during
+          ;; this comment, it will change queue->data, and so
+          ;; futex-wait returns immediately instead of sleeping.
+          ;; Ergo, no lost wakeup
+          (futex-wait (waitqueue-data-address queue)
+                      (sb!kernel:get-lisp-obj-address me)))
+      ;; 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 debugger, but this is
+      ;; better than nothing.
+      (get-mutex lock value))))
+
+
 (defun condition-notify (queue)
   "Notify one of the processes waiting on QUEUE"
   (with-spinlock (queue) (signal-queue-head queue)))
 
 (defun condition-notify (queue)
   "Notify one of the processes waiting on QUEUE"
   (with-spinlock (queue) (signal-queue-head queue)))
 
+#!+sb-futex
+(defun condition-notify/futex (queue)
+  "Notify one of the processes waiting on QUEUE."
+  (let ((me (current-thread-id)))
+    ;; no problem if >1 thread notifies during the comment in
+    ;; condition-wait: as long as the value in queue-data isn't the
+    ;; waiting thread's id, it matters not what it is
+    ;; XXX we should do something to ensure that the result of this setf
+    ;; is visible to all CPUs
+    (setf (waitqueue-data queue) me)
+    (futex-wake (waitqueue-data-address queue) 1)))
+
+#!+sb-futex
+(defun condition-broadcast/futex (queue)
+  (let ((me (current-thread-id)))
+    (setf (waitqueue-data queue) me)
+    (futex-wake (waitqueue-data-address queue) (ash 1 30))))
+
+(defun condition-broadcast (queue)
+  "Notify all of the processes waiting on QUEUE."
+  (with-spinlock (queue)
+    (map nil #'signal-thread-to-dequeue (waitqueue-data queue))))
+
+;;; Futexes may be available at compile time but not runtime, so we
+;;; default to not using them unless os_init says they're available
+(defun maybe-install-futex-functions ()
+  #!+sb-futex
+  (unless (zerop (extern-alien "linux_supports_futex" int))
+    (setf (fdefinition 'get-mutex) #'get-mutex/futex
+         (fdefinition 'release-mutex) #'release-mutex/futex
+         (fdefinition 'condition-wait) #'condition-wait/futex
+         (fdefinition 'condition-broadcast) #'condition-broadcast/futex
+         (fdefinition 'condition-notify) #'condition-notify/futex)
+    t))
 
 ;;;; multiple independent listeners
 
 
 ;;;; multiple independent listeners
 
@@ -239,170 +354,52 @@ time we reacquire LOCK and return to the caller."
   
 ;;;; job control
 
   
 ;;;; job control
 
-(defvar *background-threads-wait-for-debugger* t)
-;;; may be T, NIL, or a function called with a stream and thread id 
-;;; as its two arguments, returning NIl or T
+
+(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 init-job-control ()
+  (with-mutex (*interactive-threads-lock*)
+    (setf *interactive-threads* (list (current-thread-id)))
+    (return-from init-job-control t)))
 
 ;;; 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
 
 ;;; 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
-already the foreground thread, or transfers control to the first applicable
-restart if *BACKGROUND-THREADS-WAIT-FOR-DEBUGGER* says to do that instead"
-  (let* ((wait-p *background-threads-wait-for-debugger*)
-        (*background-threads-wait-for-debugger* nil)
-        (lock *session-lock*))
-    (when (not (eql (mutex-value lock)   (CURRENT-THREAD-ID)))
-      (when (functionp wait-p) 
-       (setf wait-p 
-             (funcall wait-p stream (CURRENT-THREAD-ID))))
-      (cond (wait-p (get-foreground))
-           (t  (invoke-restart (car (compute-restarts))))))))
-
-;;; install this with
-;;; (setf SB-INT:*REPL-PROMPT-FUN* #'sb-thread::thread-repl-prompt-fun)
-;;; One day it will be default
-(defun thread-repl-prompt-fun (out-stream)
-  (let ((lock *session-lock*))
-    (get-foreground)
-    (let ((stopped-threads (waitqueue-data lock)))
-      (when stopped-threads
-       (format out-stream "~{~&Thread ~A suspended~}~%" stopped-threads))
-      (sb!impl::repl-prompt-fun out-stream))))
-
-(defun resume-stopped-thread (id)
-  (let ((lock *session-lock*)) 
-    (with-spinlock (lock)
-      (setf (waitqueue-data lock)
-           (cons id (delete id  (waitqueue-data lock)))))
-    (release-foreground)))
-
-(defstruct rwlock
-  (name nil :type (or null simple-base-string))
-  (value 0 :type fixnum)
-  (max-readers nil :type (or fixnum null))
-  (max-writers 1 :type fixnum))
-#+nil
-(macrolet
-    ((make-rwlocking-function (lock-fn unlock-fn increment limit test)
-       (let ((do-update '(when (eql old-value
-                               (sb!vm::%instance-set-conditional
-                                lock 2 old-value new-value))
-                         (return (values t old-value))))
-            (vars `((timeout (and timeout (+ (get-internal-real-time) timeout)))
-                    old-value
-                    new-value
-                    (limit ,limit))))
-        (labels ((do-setfs (v) `(setf old-value (rwlock-value lock)
-                                 new-value (,v old-value ,increment))))
-          `(progn
-            (defun ,lock-fn (lock timeout)
-              (declare (type rwlock lock))
-              (let ,vars
-                (loop
-                 ,(do-setfs '+)
-                 (when ,test
-                   ,do-update)
-                 (when (sleep-a-bit timeout) (return nil)) ;expired
-                 )))
-            ;; unlock doesn't need timeout or test-in-range
-            (defun ,unlock-fn (lock)
-              (declare (type rwlock lock))
-              (declare (ignorable limit))
-              (let ,(cdr vars)
-                (loop
-                 ,(do-setfs '-)
-                 ,do-update))))))))
-    
-  (make-rwlocking-function %lock-for-reading %unlock-for-reading 1
-                          (rwlock-max-readers lock)
-                          (and (>= old-value 0)
-                               (or (null limit) (<= new-value limit))))
-  (make-rwlocking-function %lock-for-writing %unlock-for-writing -1
-                          (- (rwlock-max-writers lock))
-                          (and (<= old-value 0)
-                               (>= new-value limit))))
-#+nil  
-(defun get-rwlock (lock direction &optional timeout)
-  (ecase direction
-    (:read (%lock-for-reading lock timeout))
-    (:write (%lock-for-writing lock timeout))))
-#+nil
-(defun free-rwlock (lock direction)
-  (ecase direction
-    (:read (%unlock-for-reading lock))
-    (:write (%unlock-for-writing lock))))
-
-;;;; beyond this point all is commented.
-
-;;; Lock-Wait-With-Timeout  --  Internal
-;;;
-;;; Wait with a timeout for the lock to be free and acquire it for the
-;;; *current-process*.
-;;;
-#+nil
-(defun lock-wait-with-timeout (lock whostate timeout)
-  (declare (type lock lock))
-  (process-wait-with-timeout
-   whostate timeout
-   #'(lambda ()
-       (declare (optimize (speed 3)))
-       #-i486
-       (unless (lock-process lock)
-        (setf (lock-process lock) *current-process*))
-       #+i486
-       (null (kernel:%instance-set-conditional
-             lock 2 nil *current-process*)))))
-
-;;; With-Lock-Held  --  Public
-;;;
-#+nil
-(defmacro with-lock-held ((lock &optional (whostate "Lock Wait")
-                               &key (wait t) timeout)
-                         &body body)
-  "Execute the body with the lock held. If the lock is held by another
-  process then the current process waits until the lock is released or
-  an optional timeout is reached. The optional wait timeout is a time in
-  seconds acceptable to process-wait-with-timeout.  The results of the
-  body are return upon success and NIL is return if the timeout is
-  reached. When the wait key is NIL and the lock is held by another
-  process then NIL is return immediately without processing the body."
-  (let ((have-lock (gensym)))
-    `(let ((,have-lock (eq (lock-process ,lock) *current-process*)))
-      (unwind-protect
-          ,(cond ((and timeout wait)
-                  `(progn
-                     (when (and (error-check-lock-p ,lock) ,have-lock)
-                       (error "Dead lock"))
-                     (when (or ,have-lock
-                                #+i486 (null (kernel:%instance-set-conditional
-                                              ,lock 2 nil *current-process*))
-                                #-i486 (seize-lock ,lock)
-                                (if ,timeout
-                                    (lock-wait-with-timeout
-                                     ,lock ,whostate ,timeout)
-                                    (lock-wait ,lock ,whostate)))
-                       ,@body)))
-                 (wait
-                  `(progn
-                     (when (and (error-check-lock-p ,lock) ,have-lock)
-                       (error "Dead lock"))
-                     (unless (or ,have-lock
-                                #+i486 (null (kernel:%instance-set-conditional
-                                              ,lock 2 nil *current-process*))
-                                #-i486 (seize-lock ,lock))
-                       (lock-wait ,lock ,whostate))
-                     ,@body))
-                 (t
-                  `(when (or (and (recursive-lock-p ,lock) ,have-lock)
-                             #+i486 (null (kernel:%instance-set-conditional
-                                           ,lock 2 nil *current-process*))
-                             #-i486 (seize-lock ,lock))
-                     ,@body)))
-       (unless ,have-lock
-         #+i486 (kernel:%instance-set-conditional
-                 ,lock 2 *current-process* nil)
-         #-i486 (when (eq (lock-process ,lock) *current-process*)
-                  (setf (lock-process ,lock) nil)))))))
-
-
+interactive."
+  (prog1
+      (with-mutex (*interactive-threads-lock*)
+       (not (member (current-thread-id) *interactive-threads*)))
+    (get-foreground)))
 
 
+(defun thread-repl-prompt-fun (out-stream)
+  (get-foreground)
+  (let ((stopped-threads (cdr *interactive-threads*)))
+    (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*)
+     (let ((tid (current-thread-id)))
+       (when (eql (car *interactive-threads*) 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)))
+       (condition-wait
+       *interactive-threads-queue* *interactive-threads-lock* )))))
+
+(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*)
+    (let ((tid (current-thread-id)))
+      (setf *interactive-threads* (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
index 33f4b68..fde8f13 100644 (file)
@@ -122,10 +122,9 @@ time we reacquire LOCK and return to the caller."
   (signal-queue-head queue))
 
 
   (signal-queue-head queue))
 
 
-;;;; multiple independent listeners
-
-(defvar *session-lock* nil)
-
 ;;;; job control
 
 (defun debugger-wait-until-foreground-thread (stream) t)
 ;;;; job control
 
 (defun debugger-wait-until-foreground-thread (stream) t)
+(defun get-foreground () t)
+(defun release-foreground (&optional next) t)
+
index 50be15f..e1e0417 100644 (file)
@@ -1,7 +1,5 @@
 (in-package "SB!THREAD")
 
 (in-package "SB!THREAD")
 
-(defvar *session-lock*)
-
 (sb!xc:defmacro with-recursive-lock ((mutex) &body body)
   #!+sb-thread
   (with-unique-names (cfp)
 (sb!xc:defmacro with-recursive-lock ((mutex) &body body)
   #!+sb-thread
   (with-unique-names (cfp)
   #!-sb-thread
   `(progn ,@body))
 
   #!-sb-thread
   `(progn ,@body))
 
-#!+sb-thread
-(defun get-foreground ()
-  (when (not (eql (mutex-value *session-lock*) (current-thread-id)))
-    (get-mutex *session-lock*))
-  (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
-  t)
-#!-sb-thread
-(defun get-foreground () t)
-
-#!+sb-thread
-(defun release-foreground ()
-  (sb!sys:enable-interrupt sb!unix:sigint :ignore)
-  (release-mutex *session-lock*)
-  t)
-#!-sb-thread
-(defun release-foreground () t)
index 5aadeec..91bd1cb 100644 (file)
 (defun toplevel-init ()
 
   (/show0 "entering TOPLEVEL-INIT")
 (defun toplevel-init ()
 
   (/show0 "entering TOPLEVEL-INIT")
-  (setf sb!thread::*session-lock* (sb!thread:make-mutex :name "the terminal"))
+  (sb!thread::init-job-control)
   (sb!thread::get-foreground)
   (let (;; value of --sysinit option
        (sysinit nil)
   (sb!thread::get-foreground)
   (let (;; value of --sysinit option
        (sysinit nil)
index a83af66..3d14487 100644 (file)
@@ -2114,7 +2114,7 @@ search_space(lispobj *start, size_t words, lispobj *pointer)
     return (NULL);
 }
 
     return (NULL);
 }
 
-static lispobj*
+lispobj*
 search_read_only_space(lispobj *pointer)
 {
     lispobj* start = (lispobj*)READ_ONLY_SPACE_START;
 search_read_only_space(lispobj *pointer)
 {
     lispobj* start = (lispobj*)READ_ONLY_SPACE_START;
@@ -2124,7 +2124,7 @@ search_read_only_space(lispobj *pointer)
     return (search_space(start, (pointer+2)-start, pointer));
 }
 
     return (search_space(start, (pointer+2)-start, pointer));
 }
 
-static lispobj *
+lispobj *
 search_static_space(lispobj *pointer)
 {
     lispobj* start = (lispobj*)STATIC_SPACE_START;
 search_static_space(lispobj *pointer)
 {
     lispobj* start = (lispobj*)STATIC_SPACE_START;
index 1c9a1b5..d7f4a42 100644 (file)
@@ -655,13 +655,13 @@ void arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 }
 
 #ifdef LISP_FEATURE_SB_THREAD
 }
 
 #ifdef LISP_FEATURE_SB_THREAD
-void handle_rt_signal(int num, siginfo_t *info, void *v_context)
+void interrupt_thread_handler(int num, siginfo_t *info, void *v_context)
 {
     os_context_t *context = (os_context_t*)arch_os_get_context(&v_context);
     struct thread *th=arch_os_get_current_thread();
     struct interrupt_data *data=
        th ? th->interrupt_data : global_interrupt_data;
 {
     os_context_t *context = (os_context_t*)arch_os_get_context(&v_context);
     struct thread *th=arch_os_get_current_thread();
     struct interrupt_data *data=
        th ? th->interrupt_data : global_interrupt_data;
-    if(maybe_defer_handler(handle_rt_signal,data,num,info,context)){
+    if(maybe_defer_handler(interrupt_thread_handler,data,num,info,context)){
        return ;
     }
     arrange_return_to_lisp_function(context,info->si_value.sival_int);
        return ;
     }
     arrange_return_to_lisp_function(context,info->si_value.sival_int);
index 35e221d..11eca74 100644 (file)
@@ -51,7 +51,7 @@ extern void interrupt_internal_error(int, siginfo_t*, os_context_t*,
 extern boolean handle_control_stack_guard_triggered(os_context_t *,void *);
 extern boolean interrupt_maybe_gc(int, siginfo_t*, void*);
 #ifdef LISP_FEATURE_SB_THREAD
 extern boolean handle_control_stack_guard_triggered(os_context_t *,void *);
 extern boolean interrupt_maybe_gc(int, siginfo_t*, void*);
 #ifdef LISP_FEATURE_SB_THREAD
-extern void handle_rt_signal(int, siginfo_t*, void*);
+extern void interrupt_thread_handler(int, siginfo_t*, void*);
 extern void sig_stop_for_gc_handler(int, siginfo_t*, void*);
 #endif
 extern void undoably_install_low_level_interrupt_handler (int signal,
 extern void sig_stop_for_gc_handler(int, siginfo_t*, void*);
 #endif
 extern void undoably_install_low_level_interrupt_handler (int signal,
index 6e391f2..03d950f 100644 (file)
 #include "thread.h"
 size_t os_vm_page_size;
 
 #include "thread.h"
 size_t os_vm_page_size;
 
+#ifdef LISP_FEATURE_SB_FUTEX
+#include <asm/unistd.h>
+#include <errno.h>
+
+/* values taken from the kernel's linux/futex.h.  This header file
+   doesn't exist in userspace, which is our excuse for not grovelling
+   them automatically */
+#define FUTEX_WAIT (0)
+#define FUTEX_WAKE (1)
+#define FUTEX_FD (2)
+#define FUTEX_REQUEUE (3)
+
+#define __NR_sys_futex __NR_futex
+
+_syscall4(int,sys_futex,
+         int *, futex,
+         int, op,
+         int, val,
+         struct timespec *, rel);
+#endif
+
 #include "gc.h"
 \f
 int linux_sparc_siginfo_bug = 0;
 #include "gc.h"
 \f
 int linux_sparc_siginfo_bug = 0;
+int linux_supports_futex=0;
 
 void os_init(void)
 {
     /* Conduct various version checks: do we have enough mmap(), is
      * this a sparc running 2.2, can we do threads? */
 
 void os_init(void)
 {
     /* Conduct various version checks: do we have enough mmap(), is
      * this a sparc running 2.2, can we do threads? */
-    {
-        struct utsname name;
-       int major_version;
-       int minor_version;
-       char *p;
-       uname(&name);
-       p=name.release;  
-       major_version = atoi(p);
-       p=strchr(p,'.')+1;
-       minor_version = atoi(p);
-       if (major_version<2) {
-           lose("linux kernel version too old: major version=%d (can't run in version < 2.0.0)",
-                major_version);
-       }
+    int *futex=0;
+    struct utsname name;
+    int major_version;
+    int minor_version;
+    char *p;
+    uname(&name);
+    p=name.release;  
+    major_version = atoi(p);
+    p=strchr(p,'.')+1;
+    minor_version = atoi(p);
+    if (major_version<2) {
+       lose("linux kernel version too old: major version=%d (can't run in version < 2.0.0)",
+            major_version);
+    }
+    if (!(major_version>2 || minor_version >= 4)) {
 #ifdef LISP_FEATURE_SB_THREAD
 #ifdef LISP_FEATURE_SB_THREAD
-       if ((major_version <2) || (major_version==2 && minor_version < 4)) {
-           lose("linux kernel 2.4 required for thread-enabled SBCL");
-       }
+       lose("linux kernel 2.4 required for thread-enabled SBCL");
 #endif
 #ifdef LISP_FEATURE_SPARC
 #endif
 #ifdef LISP_FEATURE_SPARC
-       if ((major_version <2) || (major_version==2 && minor_version < 4)) {
-           FSHOW((stderr,"linux kernel %d.%d predates 2.4;\n enabling workarounds for SPARC kernel bugs in signal handling.\n", major_version,minor_version));
-           linux_sparc_siginfo_bug = 1;
-       }
+       FSHOW((stderr,"linux kernel %d.%d predates 2.4;\n enabling workarounds for SPARC kernel bugs in signal handling.\n", major_version,minor_version));
+       linux_sparc_siginfo_bug = 1;
 #endif
     }
 #endif
     }
-
-    os_vm_page_size = getpagesize();
-    /* This could just as well be in arch_init(), but it's not. */
-#ifdef LISP_FEATURE_X86
-    /* FIXME: This used to be here.  However, I have just removed it
-       with no apparent ill effects (it may be that earlier kernels
-       started up a process with a different set of traps, or
-       something?) Find out what this was meant to do, and reenable it
-       or delete it if possible. -- CSR, 2002-07-15 */
-    /* SET_FPU_CONTROL_WORD(0x1372|4|8|16|32);  no interrupts */
+#ifdef LISP_FEATURE_SB_FUTEX
+    futex_wait(futex,-1);
+    if(errno!=ENOSYS) linux_supports_futex=1;
 #endif
 #endif
+    os_vm_page_size = getpagesize();
 }
 
 
 }
 
 
@@ -264,11 +277,21 @@ os_install_interrupt_handlers(void)
                                                 sigsegv_handler);
 #ifdef LISP_FEATURE_SB_THREAD
     undoably_install_low_level_interrupt_handler(SIG_INTERRUPT_THREAD,
                                                 sigsegv_handler);
 #ifdef LISP_FEATURE_SB_THREAD
     undoably_install_low_level_interrupt_handler(SIG_INTERRUPT_THREAD,
-                                                handle_rt_signal);
+                                                interrupt_thread_handler);
     undoably_install_low_level_interrupt_handler(SIG_STOP_FOR_GC,
                                                 sig_stop_for_gc_handler);
     undoably_install_low_level_interrupt_handler(SIG_STOP_FOR_GC,
                                                 sig_stop_for_gc_handler);
+    if(!linux_supports_futex)
+       undoably_install_low_level_interrupt_handler(SIG_DEQUEUE,
+                                                    sigcont_handler);
 #endif
 #endif
-    undoably_install_low_level_interrupt_handler(SIG_DEQUEUE,
-                                                sigcont_handler);
 }
 
 }
 
+#ifdef LISP_FEATURE_SB_FUTEX
+int futex_wait(int *lock_word, int oldval) {
+    int t= sys_futex(lock_word,FUTEX_WAIT,oldval, 0);
+    return t;
+}
+int futex_wake(int *lock_word, int n){
+    return sys_futex(lock_word,FUTEX_WAKE,n,0);
+}
+#endif
index 1055b2d..05729ae 100644 (file)
@@ -37,8 +37,8 @@ typedef int os_vm_prot_t;
 #define OS_VM_PROT_EXECUTE PROT_EXEC
 
 #define SIG_MEMORY_FAULT SIGSEGV
 #define OS_VM_PROT_EXECUTE PROT_EXEC
 
 #define SIG_MEMORY_FAULT SIGSEGV
-#define SIG_INTERRUPT_THREAD SIGRTMIN
+
+#define SIG_INTERRUPT_THREAD (SIGRTMIN)
 #define SIG_STOP_FOR_GC (SIGRTMIN+1)
 #define SIG_DEQUEUE (SIGRTMIN+2)
 
 #define SIG_STOP_FOR_GC (SIGRTMIN+1)
 #define SIG_DEQUEUE (SIGRTMIN+2)
 
-
index 3092fc0..8ac0519 100644 (file)
@@ -50,6 +50,8 @@ new_thread_trampoline(struct thread *th)
        fprintf(stderr, "/continue\n");
     }
     th->unbound_marker = UNBOUND_MARKER_WIDETAG;
        fprintf(stderr, "/continue\n");
     }
     th->unbound_marker = UNBOUND_MARKER_WIDETAG;
+    if(arch_os_thread_init(th)==0) 
+       return 1;               /* failure.  no, really */
 #ifdef LISP_FEATURE_SB_THREAD
     /* wait here until our thread is linked into all_threads: see below */
     while(th->pid<1) sched_yield();
 #ifdef LISP_FEATURE_SB_THREAD
     /* wait here until our thread is linked into all_threads: see below */
     while(th->pid<1) sched_yield();
@@ -58,8 +60,7 @@ new_thread_trampoline(struct thread *th)
        lose("th->pid not set up right");
 #endif
 
        lose("th->pid not set up right");
 #endif
 
-    if(arch_os_thread_init(th)==0) 
-       return 1;               /* failure.  no, really */
+    th->state=STATE_RUNNING;
 #if !defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_X86)
     return call_into_lisp_first_time(function,args,0);
 #else
 #if !defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_X86)
     return call_into_lisp_first_time(function,args,0);
 #else
@@ -136,7 +137,7 @@ pid_t create_thread(lispobj initial_function) {
     th->binding_stack_pointer=th->binding_stack_start;
     th->this=th;
     th->pid=0;
     th->binding_stack_pointer=th->binding_stack_start;
     th->this=th;
     th->pid=0;
-    th->state=STATE_RUNNING;
+    th->state=STATE_STOPPED;
 #ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD
     th->alien_stack_pointer=((void *)th->alien_stack_start
                             + ALIEN_STACK_SIZE-4); /* naked 4.  FIXME */
 #ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD
     th->alien_stack_pointer=((void *)th->alien_stack_start
                             + ALIEN_STACK_SIZE-4); /* naked 4.  FIXME */
@@ -145,9 +146,7 @@ pid_t create_thread(lispobj initial_function) {
 #endif
 #ifdef LISP_FEATURE_X86
     th->pseudo_atomic_interrupted=0;
 #endif
 #ifdef LISP_FEATURE_X86
     th->pseudo_atomic_interrupted=0;
-    /* runtime.c used to set PSEUDO_ATOMIC_ATOMIC =1 globally.  I'm not
-     * sure why, but it appears to help */
-    th->pseudo_atomic_atomic=make_fixnum(1);
+    th->pseudo_atomic_atomic=0;
 #endif
 #ifdef LISP_FEATURE_GENCGC
     gc_set_region_empty(&th->alloc_region);
 #endif
 #ifdef LISP_FEATURE_GENCGC
     gc_set_region_empty(&th->alloc_region);
@@ -297,6 +296,12 @@ int interrupt_thread(pid_t pid, lispobj function)
     return sigqueue(pid, SIG_INTERRUPT_THREAD, sigval);
 }
 
     return sigqueue(pid, SIG_INTERRUPT_THREAD, sigval);
 }
 
+int signal_thread_to_dequeue (pid_t pid)
+{
+    return kill (pid, SIG_DEQUEUE);
+}
+
+
 /* stopping the world is a two-stage process.  From this thread we signal 
  * all the others with SIG_STOP_FOR_GC.  The handler for this thread does
  * the usual pseudo-atomic checks (we don't want to stop a thread while 
 /* stopping the world is a two-stage process.  From this thread we signal 
  * all the others with SIG_STOP_FOR_GC.  The handler for this thread does
  * the usual pseudo-atomic checks (we don't want to stop a thread while 
@@ -309,39 +314,36 @@ void gc_stop_the_world()
 {
     /* stop all other threads by sending them SIG_STOP_FOR_GC */
     struct thread *p,*th=arch_os_get_current_thread();
 {
     /* stop all other threads by sending them SIG_STOP_FOR_GC */
     struct thread *p,*th=arch_os_get_current_thread();
-    struct thread *tail=0;
+    pid_t old_pid;
     int finished=0;
     do {
        get_spinlock(&all_threads_lock,th->pid);
     int finished=0;
     do {
        get_spinlock(&all_threads_lock,th->pid);
-       if(tail!=all_threads) {
-           /* new threads always get consed onto the front of all_threads,
-            * and may be created by any thread that we haven't signalled
-            * yet or hasn't received our signal and stopped yet.  So, check
-            * for them on each time around */
-           for(p=all_threads;p!=tail;p=p->next) {
-               if(p==th) continue;
-               /* if the head of all_threads is removed during
-                * gc_stop_the_world, we may take a second trip through the 
-                * list and end up counting twice as many threads to wait for
-                * as actually exist */
-               if(p->state!=STATE_RUNNING) continue;
-               countdown_to_gc++;
-               p->state=STATE_STOPPING;
-               /* Note no return value check from kill().  If the
-                * thread had been reaped already, we kill it and
-                * increment countdown_to_gc anyway.  This is to avoid
-                * complicating the logic in destroy_thread, which would 
-                * otherwise have to know whether the thread died before or
-                * after it was killed
-                */
-               kill(p->pid,SIG_STOP_FOR_GC);
-           }
-           tail=all_threads;
-       } else {
-           finished=(countdown_to_gc==0);
+       for(p=all_threads,old_pid=p->pid; p; p=p->next) {
+           if(p==th) continue;
+           if(p->state!=STATE_RUNNING) continue;
+           countdown_to_gc++;
+           p->state=STATE_STOPPING;
+           /* Note no return value check from kill().  If the
+            * thread had been reaped already, we kill it and
+            * increment countdown_to_gc anyway.  This is to avoid
+            * complicating the logic in destroy_thread, which would 
+            * otherwise have to know whether the thread died before or
+            * after it was killed
+            */
+           kill(p->pid,SIG_STOP_FOR_GC);
        }
        release_spinlock(&all_threads_lock);
        sched_yield();
        }
        release_spinlock(&all_threads_lock);
        sched_yield();
+       /* if everything has stopped, and there is no possibility that
+        * a new thread has been created, we're done.  Otherwise go
+        * round again and signal anything that sprang up since last
+        * time  */
+       if(old_pid==all_threads->pid) {
+           finished=1;
+           for_each_thread(p) 
+               finished = finished &&
+               ((p==th) || (p->state==STATE_STOPPED));
+       }
     } while(!finished);
 }
 
     } while(!finished);
 }
 
index 9b51cc0..543c7e0 100644 (file)
@@ -72,8 +72,7 @@ void arch_skip_instruction(os_context_t *context)
     int vlen;
     int code;
 
     int vlen;
     int code;
 
-    FSHOW((stderr, "/[arch_skip_inst at %x]\n", *os_context_pc_addr(context)));
-
+    
     /* Get and skip the Lisp interrupt code. */
     code = *(char*)(*os_context_pc_addr(context))++;
     switch (code)
     /* Get and skip the Lisp interrupt code. */
     code = *(char*)(*os_context_pc_addr(context))++;
     switch (code)
@@ -192,6 +191,7 @@ sigtrap_handler(int signal, siginfo_t *info, void *void_context)
     int code = info->si_code;
     os_context_t *context = (os_context_t*)void_context;
     unsigned int trap;
     int code = info->si_code;
     os_context_t *context = (os_context_t*)void_context;
     unsigned int trap;
+    sigset_t ss;
 
     if (single_stepping && (signal==SIGTRAP))
     {
 
     if (single_stepping && (signal==SIGTRAP))
     {
@@ -242,6 +242,9 @@ sigtrap_handler(int signal, siginfo_t *info, void *void_context)
     case trap_PendingInterrupt:
        FSHOW((stderr, "/<trap pending interrupt>\n"));
        arch_skip_instruction(context);
     case trap_PendingInterrupt:
        FSHOW((stderr, "/<trap pending interrupt>\n"));
        arch_skip_instruction(context);
+       sigemptyset(&ss);
+       sigaddset(&ss,SIGTRAP);
+       sigprocmask(SIG_UNBLOCK,&ss,0);
        interrupt_handle_pending(context);
        break;
 
        interrupt_handle_pending(context);
        break;
 
index e0973ff..4b59604 100644 (file)
@@ -18,7 +18,6 @@
 ;;; For one of the interupt-thread tests, we want a foreign function
 ;;; that does not make syscalls
 
 ;;; For one of the interupt-thread tests, we want a foreign function
 ;;; that does not make syscalls
 
-(setf SB-INT:*REPL-PROMPT-FUN* #'sb-thread::thread-repl-prompt-fun)
 (with-open-file (o "threads-foreign.c" :direction :output)
   (format o "void loop_forever() { while(1) ; }~%"))
 (sb-ext:run-program    
 (with-open-file (o "threads-foreign.c" :direction :output)
   (format o "void loop_forever() { while(1) ; }~%"))
 (sb-ext:run-program    
 ;;; elementary "can we get a lock and release it again"
 (let ((l (make-mutex :name "foo"))
       (p (current-thread-id)))
 ;;; elementary "can we get a lock and release it again"
 (let ((l (make-mutex :name "foo"))
       (p (current-thread-id)))
-  (assert (eql (mutex-value l) nil))
-  (assert (eql (mutex-lock l) 0))
+  (assert (eql (mutex-value l) nil) nil "1")
+  (assert (eql (mutex-lock l) 0) nil "2")
   (sb-thread:get-mutex l)
   (sb-thread:get-mutex l)
-  (assert (eql (mutex-value l) p))
-  (assert (eql (mutex-lock l) 0))
+  (assert (eql (mutex-value l) p) nil "3")
+  (assert (eql (mutex-lock l) 0) nil "4")
   (sb-thread:release-mutex l)
   (sb-thread:release-mutex l)
-  (assert (eql (mutex-value l) nil))
-  (assert (eql (mutex-lock l) 0)))
+  (assert (eql (mutex-value l) nil) nil "5")
+  (assert (eql (mutex-lock l) 0)  nil "6")
+  (describe l))
 
 (let ((queue (make-waitqueue :name "queue"))
       (lock (make-mutex :name "lock")))
 
 (let ((queue (make-waitqueue :name "queue"))
       (lock (make-mutex :name "lock")))
       (condition-notify queue))
     (sleep 1)))
 
       (condition-notify queue))
     (sleep 1)))
 
+(let ((mutex (make-mutex :name "contended")))
+  (labels ((run ()
+            (let ((me (current-thread-id)))
+              (dotimes (i 100)
+                (with-mutex (mutex)
+                  (sleep .1)
+                  (assert (eql (mutex-value mutex) me)))
+                (assert (not (eql (mutex-value mutex) me))))
+              (format t "done ~A~%" (current-thread-id)))))
+    (let ((kid1 (make-thread #'run))
+         (kid2 (make-thread #'run)))
+      (format t "contention ~A ~A~%" kid1 kid2))))
 
 (defun test-interrupt (function-to-interrupt &optional quit-p)
   (let ((child  (make-thread function-to-interrupt)))
 
 (defun test-interrupt (function-to-interrupt &optional quit-p)
   (let ((child  (make-thread function-to-interrupt)))
   (terminate-thread child))
 
 (defun alloc-stuff () (copy-list '(1 2 3 4 5)))
   (terminate-thread child))
 
 (defun alloc-stuff () (copy-list '(1 2 3 4 5)))
+
 (let ((c (test-interrupt (lambda () (loop (alloc-stuff))))))
   ;; NB this only works on x86: other ports don't have a symbol for
   ;; pseudo-atomic atomicity
 (let ((c (test-interrupt (lambda () (loop (alloc-stuff))))))
   ;; NB this only works on x86: other ports don't have a symbol for
   ;; pseudo-atomic atomicity
+  (format t "new thread ~A~%" c)
   (dotimes (i 100)
     (sleep (random 1d0))
     (interrupt-thread c
   (dotimes (i 100)
     (sleep (random 1d0))
     (interrupt-thread c
                        (assert (zerop SB-KERNEL:*PSEUDO-ATOMIC-ATOMIC*)))))
   (terminate-thread c))
 
                        (assert (zerop SB-KERNEL:*PSEUDO-ATOMIC-ATOMIC*)))))
   (terminate-thread c))
 
-;; I'm not sure that this one is always successful.  Note race potential:
-;; I haven't checked if decf is atomic here
-(let ((done 2))
-  (make-thread (lambda () (dotimes (i 100) (sb-ext:gc)) (decf done)))
-  (make-thread (lambda () (dotimes (i 25) (sb-ext:gc :full t)) (decf done)))
+(format t "~&interrupt test done~%")
+
+(let (a-done b-done)
+  (make-thread (lambda ()
+                (dotimes (i 100) 
+                  (sb-ext:gc) (princ "\\") (force-output) )
+                (setf a-done t)))
+  (make-thread (lambda ()
+                (dotimes (i 25) 
+                  (sb-ext:gc :full t)
+                  (princ "/") (force-output))
+                (setf b-done t)))
   (loop
   (loop
-   (when (zerop done) (return))
+   (when (and a-done b-done) (return))
    (sleep 1)))
    (sleep 1)))
+(format t "~&gc test done~%")
+
+#|  ;; a cll post from eric marsden
+| (defun crash ()
+|   (setq *debugger-hook*
+|         (lambda (condition old-debugger-hook)
+|           (debug:backtrace 10)
+|           (unix:unix-exit 2)))
+|   #+live-dangerously
+|   (mp::start-sigalrm-yield)
+|   (flet ((roomy () (loop (with-output-to-string (*standard-output*) (room)))))
+|     (mp:make-process #'roomy)
+|     (mp:make-process #'roomy)))
+|#
 
 ;; give the other thread time to die before we leave, otherwise the
 ;; overall exit status is 0, not 104
 
 ;; give the other thread time to die before we leave, otherwise the
 ;; overall exit status is 0, not 104
index 2254aa3..facc3f8 100644 (file)
@@ -55,7 +55,7 @@ main(int argc, char *argv[])
     /* don't need no steenking hand-editing */
     printf(
 ";;;; This is an automatically generated file, please do not hand-edit it.\n\
     /* don't need no steenking hand-editing */
     printf(
 ";;;; This is an automatically generated file, please do not hand-edit it.\n\
-;;;; See the program \"grovel_headers.c\".\n\
+;;;; See the program \"grovel-headers.c\".\n\
 \n\
 ");
 
 \n\
 ");
 
@@ -188,9 +188,5 @@ main(int argc, char *argv[])
     DEFSIGNAL(SIGXCPU);
     DEFSIGNAL(SIGXFSZ);
 #endif
     DEFSIGNAL(SIGXCPU);
     DEFSIGNAL(SIGXFSZ);
 #endif
-#ifdef LISP_FEATURE_SB_THREAD
-    /* FIXME OAOOM alert: this information is duplicated in linux-os.h */
-    defconstant("sig-dequeue",SIGRTMIN+2);
-#endif
     return 0;
 }
     return 0;
 }
index 7d641d8..7360bd3 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".)
 ;;; 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.4"
+"0.8.6.5"