0.8.3.1
authorDaniel Barlow <dan@telent.net>
Mon, 25 Aug 2003 21:00:00 +0000 (21:00 +0000)
committerDaniel Barlow <dan@telent.net>
Mon, 25 Aug 2003 21:00:00 +0000 (21:00 +0000)
Merge stop_the_world branch: a new scheme for stopping threads
during GC, that avoids the use of ptrace and lets any of the
threads stop any of the others.  This is a summary log entry:
see branch commits for details

New C functions maybe_defer_handler and run_deferred_handler,
which encapsulate a lot of the "is it safe to run this handler
now?  no, ok, copy its siginfo somewhere safe and do it later"
cruft that's presently done ad hoc.

Stack scavenging now looks for esp in the most recent
interrupt context for each thread.  Requires that threads
save said interrupt contexts in sig_stop_for_gc_handler

Clean up some compiler warnings in gencgc.c

Lisp-level changes: approximately a reversion to old-style (or
single-threaded) GC.  Haven't actually added the gc hooks back
in yet, but now there's at least a place for them to go.

Lock around SUB-GC to remove window that may allow two threads
to attempt to collect at once.

WITHOUT-INTERRUPTS around SUB-GC to protect c-level spinlocks
used in gc_{stop,start}_the_world

(C-level spinlocks are just integers manipulated by get_spinlock(),
release_spinlock().  There's no unwind-protect or anything
involved in their use, so a thread interrupted when it's
holding one of these will continue to hold it)

Remove #if 0 from around the copying of sigmask in
        undo_fake_foreign_function_call.  Replace sizeof(sigmask_t)
        with an expression involving the value of NSIG and the rash
        assumption that sigset_t is a bitmask.

Moved get_spinlock into foo-arch.h and made it static inline.
Added release_spinlock for parity

Delete irritating message from sigcont_handler

New test cases

29 files changed:
doc/internals-notes/threading-specials [new file with mode: 0644]
src/code/gc.lisp
src/code/target-thread.lisp
src/runtime/alpha-arch.c
src/runtime/alpha-arch.h
src/runtime/backtrace.c
src/runtime/gc.h
src/runtime/gencgc-internal.h
src/runtime/gencgc.c
src/runtime/hppa-arch.c
src/runtime/hppa-arch.h
src/runtime/interrupt.c
src/runtime/interrupt.h
src/runtime/linux-os.c
src/runtime/linux-os.h
src/runtime/mips-arch.c
src/runtime/mips-arch.h
src/runtime/ppc-arch.c
src/runtime/ppc-arch.h
src/runtime/runtime.c
src/runtime/sparc-arch.c
src/runtime/sparc-arch.h
src/runtime/thread.c
src/runtime/thread.h
src/runtime/x86-arch.c
src/runtime/x86-arch.h
src/runtime/x86-linux-os.c
tests/threads.impure.lisp
version.lisp-expr

diff --git a/doc/internals-notes/threading-specials b/doc/internals-notes/threading-specials
new file mode 100644 (file)
index 0000000..3e8c597
--- /dev/null
@@ -0,0 +1,1105 @@
+
+Things in SBCL that look like special variables (list created by
+(apropos "*")) and could conceivably indicate potental thread safety
+issues.  Organised by functional area.
+
+The goals are
+
+       (1) classify the unclassified symbols (in such a way as to
+       make sense for 2)
+       
+       (2) read through the subsystems identified looking for places
+       where global state is mutated.  Use this list of symbols as a
+       guide, but be open to the possibility that other state 
+       (e.g. closures, foreign variables) is touched too.
+
+       When looking at a particular symbol, please annotate it in
+       this file when you figure out what it's for and how it's
+       changed.  To give us some confidence that we have reasonable
+       coverage, please don't just delete it if it looks ok.  Symbols
+       shoud only be deleted here if they're obviously not specials
+       at all (e.g. functions with * in their names), or if the same
+       commit actually rids SBCL of the symbol itself (as we may soon
+       do for e.g. SB-SYS:*TASK-SELF*).  Otherwise, just add a
+       comment saying "read-only" or "unused" or whatever.
+
+       (3) anything else that comes to mind as potential trouble
+        spots.  Global state in closures, etc etc
+           
+       (4) suggest strategies (e.g. rewrites of affected code, suitable
+       locks and lock acquitision/release places) to fix the issues
+       revealed in (2) and (3)
+       
+       (5) implement same
+
+       Candidates may attempt any of the above simultaneously.
+       Points will be awarded for style
+
+
+Summary so far:
+
+= loop
+= PCL            (probably ok if PCL authors did *without-interrupts* right)
+= debugger
+= profiler
+= disassembler
+= assembler       (I assume protected by *big-compiler-lock*)
+= unix interface  (apparently ok)
+= toplevel/environment stuff
+= the formatter & pretty printer  (two vars need checking)
+= compiler        (protected by *big-compiler-lock*) 
+= fasloader       (protected by *big-compiler-lock*) 
+= runtime stuff - gc, control stacks, interrupts etc
+= backend constants 
+= dead
+= unclassified
+
+==================
+
+= loop
+
+I suspect that stuff in sb-loop is only ever frobbed at
+macroexpand/compile time anyway, so covered by *big-compiler-lock*.
+Haven't thought about this too hard, though
+
+SB-LOOP::*LOOP-PROLOGUE*
+SB-LOOP::*LOOP-SOURCE-CODE*
+SB-LOOP::*LOOP-MINIMAX-TYPE-INFINITIES-ALIST* 
+SB-LOOP::*LOOP-NAMES*
+SB-LOOP::*LOOP-BODY*
+SB-LOOP::*LOOP-AFTER-BODY*
+SB-LOOP::*LOOP-DESETQ-CROCKS*
+SB-LOOP::*LOOP-EPILOGUE*
+SB-LOOP::*LOOP-EMITTED-BODY*
+SB-LOOP::*LOOP-NEVER-STEPPED-VAR*
+SB-LOOP::*LOOP-NAMED-VARS*
+SB-LOOP::*LOOP-ITERATION-VARS*
+SB-LOOP::*LOOP-WHEN-IT-VAR*
+SB-LOOP::*LOOP-BEFORE-LOOP*
+SB-LOOP::*ESTIMATE-CODE-SIZE-PUNT* 
+SB-LOOP::*LOOP-WRAPPERS*
+SB-LOOP::*LOOP-DESETQ-TEMPORARY* 
+SB-LOOP::*LOOP-MACRO-ENVIRONMENT*
+SB-LOOP::*LOOP-VARS*
+SB-LOOP::*IGNORES*
+SB-LOOP::*LOOP-SOURCE-CONTEXT*
+SB-LOOP::*LOOP-AFTER-EPILOGUE*
+SB-LOOP::*LOOP-DECLARATIONS*
+SB-LOOP::*LOOP-FINAL-VALUE-CULPRIT*
+SB-LOOP::*LOOP-COLLECTION-CRUFT*
+SB-LOOP::*SPECIAL-CODE-SIZES* 
+SB-LOOP::*LOOP-UNIVERSE*
+SB-LOOP::*LOOP-DUPLICATE-CODE* 
+SB-LOOP::*LOOP-INSIDE-CONDITIONAL*
+SB-LOOP::*LOOP-ORIGINAL-SOURCE-CODE*
+SB-LOOP::*LOOP-ITERATION-FLAG-VAR* 
+SB-LOOP::*LOOP-ANSI-UNIVERSE* 
+SB-LOOP::*LOOP-BIND-STACK*
+
+= PCL
+
+The PCL authors thought a bit about thread safety, adding
+(without-interrupts ...) in some places to protect critical forms.
+We've implemented their without-interrupts macro as an acquitision of
+*pcl-lock*, so we hope they've done it properly.
+
+SB-PCL::*STRUCTURE-TYPEP-COST* 
+SB-PCL::*SLOT-NAME-LISTS-OUTER* 
+SB-PCL::*THE-WRAPPER-OF-T* 
+SB-PCL::*CREATE-CLASSES-FROM-INTERNAL-STRUCTURE-DEFINITIONS-P* 
+SB-PCL::*WRITERS-FOR-THIS-DEFCLASS*
+SB-PCL::*BOOT-STATE* 
+SB-PCL::*THE-WRAPPER-OF-BIT-VECTOR* 
+SB-PCL::*EFFECTIVE-METHOD-TABLE* 
+SB-PCL::*THE-WRAPPER-OF-COMPLEX-DOUBLE-FLOAT* 
+SB-PCL::*THE-CLASS-COMPLEX-DOUBLE-FLOAT* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-SINGLE-FLOAT* 
+SB-PCL::*CACHE-LOOKUP-COST* 
+SB-PCL::*SLOT-NAME-LISTS-INNER* 
+SB-PCL::*MF2P* 
+SB-PCL::*THE-CLASS-RATIONAL* 
+SB-PCL::WRAPPER-CLASS* 
+SB-PCL::*PREVIOUS-NWRAPPERS* 
+SB-PCL::*MF1* 
+SB-PCL::*THE-CLASS-STANDARD-OBJECT* 
+SB-PCL::*THE-CLASS-CONDITION-DIRECT-SLOT-DEFINITION* 
+SB-PCL::*THE-CLASS-RATIO* 
+SB-PCL::*SHOW-MAKE-UNORDERED-METHODS-EMF-CALLS* 
+SB-PCL::*ALL-CTORS* 
+SB-PCL::*SUBTYPEP 
+SB-PCL::*THE-WRAPPER-OF-LRA* 
+SB-PCL::*INITFUNCTIONS-FOR-THIS-DEFCLASS*
+SB-PCL::*STANDARD-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*EMPTY-PV* 
+SB-PCL::*CACHE-EXPAND-THRESHOLD* 
+SB-PCL::*THE-CLASS-INTEGER* 
+SB-PCL::*SGF-DFUN-STATE-INDEX* 
+SB-PCL::*ALL-PV-TABLE-LIST* 
+SB-PCL::*THE-WRAPPER-OF-RANDOM-CLASS* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-COMPLEX-SINGLE-FLOAT* 
+SB-PCL::*THE-CLASS-FDEFN* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-UNSIGNED-BYTE-32* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-VECTOR* 
+SB-PCL::*CHECK-CACHE-P* 
+SB-PCL::*SGF-WRAPPER* 
+SB-PCL::*CONDITION-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-BIT-VECTOR* 
+SB-PCL::*THE-CLASS-SPECIALIZER* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-COMPLEX-DOUBLE-FLOAT* 
+SB-PCL::*THE-CLASS-STANDARD-CLASS* 
+SB-PCL::*THE-CLASS-STANDARD-ACCESSOR-METHOD* 
+SB-PCL::*THE-CLASS-STANDARD-READER-METHOD* 
+SB-PCL::*THE-WRAPPER-OF-DOUBLE-FLOAT* 
+SB-PCL::*NON-BUILT-IN-TYPEP-COST* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-UNSIGNED-BYTE-8* 
+SB-PCL::*THE-CLASS-DOCUMENTATION-MIXIN* 
+SB-PCL::*EMF-CALL-TRACE-SIZE* 
+SB-PCL::*OLD-C-A-M-GF-METHODS* 
+SB-PCL::*EMPTY-CACHE* 
+SB-PCL::*THE-WRAPPER-OF-SYMBOL* 
+SB-PCL::*THE-CLASS-STANDARD-METHOD-COMBINATION* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-DOUBLE-FLOAT* 
+SB-PCL::*THE-CLASS-METHOD* 
+SB-PCL::*PRECOMPILING-LAP* 
+SB-PCL::*STRUCTURE-SLOT-BOUNDP-USING-CLASS-METHOD* 
+SB-PCL::*ENABLE-DFUN-CONSTRUCTOR-CACHING* 
+SB-PCL::*THE-CLASS-CONDITION-EFFECTIVE-SLOT-DEFINITION* 
+SB-PCL::*THE-WRAPPER-OF-STRUCTURE-OBJECT* 
+SB-PCL::*THE-CLASS-WEAK-POINTER* 
+SB-PCL::*STANDARD-SLOT-BOUNDP-USING-CLASS-METHOD* 
+SB-PCL::*THE-CLASS-FUNCALLABLE-STANDARD-OBJECT* 
+SB-PCL::*VAR-DECLARATIONS-WITH-ARG* 
+SB-PCL::*PV-TABLE-CACHE-UPDATE-INFO* 
+SB-PCL::*THE-WRAPPER-OF-STRING* 
+SB-PCL::*THE-CLASS-CONDITION* 
+SB-PCL::*THE-CLASS-PCL-CLASS* 
+SB-PCL::*THE-CLASS-CLASS* 
+SB-PCL::*PCL-LOCK*                         ; protecting the rest
+SB-PCL::*EARLY-P* 
+SB-PCL::*PCL-CLASS-BOOT* 
+SB-PCL::*THE-CLASS-EFFECTIVE-SLOT-DEFINITION* 
+SB-PCL::*THE-WRAPPER-OF-CONS* 
+SB-PCL::*THE-CLASS-LRA* 
+SB-PCL::*THE-CLASS-EQL-SPECIALIZER* 
+SB-PCL::*EQL-SPECIALIZER-METHODS* 
+SB-PCL::*THE-WRAPPER-OF-FLOAT* 
+SB-PCL::*INITIAL-PV-TABLE* 
+SB-PCL::*THE-CLASS-COMPLEX-SINGLE-FLOAT* 
+SB-PCL::*STRUCTURE-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*EMF-CALL-TRACE* 
+SB-PCL::*IN-OBSOLETE-INSTANCE-TRAP* 
+SB-PCL::*BUILT-IN-CLASS-SYMBOLS* 
+SB-PCL::*FREE-HASH-TABLES* 
+SB-PCL::*THE-WRAPPER-OF-WEAK-POINTER* 
+SB-PCL::*THE-CLASS-RANDOM-CLASS* 
+SB-PCL::*PVS* 
+SB-PCL::*THE-WRAPPER-OF-REAL* 
+SB-PCL::*LONG-METHOD-COMBINATION-FUNCTIONS* 
+SB-PCL::*EMIT-FUNCTION-P* 
+SB-PCL::*THE-CLASS-DOUBLE-FLOAT* 
+SB-PCL::*NAME->CLASS->SLOTD-TABLE* 
+SB-PCL::*DFUN-COUNT* 
+SB-PCL::*COMPUTE-STD-CPL-CLASS->ENTRY-TABLE-SIZE* 
+SB-PCL::*THE-CLASS-SYMBOL* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-SIGNED-BYTE-16* 
+SB-PCL::*THE-CLASS-STANDARD-DIRECT-SLOT-DEFINITION* 
+SB-PCL::*THE-CLASS-EXACT-CLASS-SPECIALIZER* 
+SB-PCL::*THE-CLASS-FLOAT* 
+SB-PCL::*THE-CLASS-BASE-CHAR* 
+SB-PCL::*PV-KEY-TO-PV-TABLE-TABLE* 
+SB-PCL::*THE-CLASS-STD-CLASS* 
+SB-PCL::*THE-CLASS-SLOT-OBJECT* 
+SB-PCL::*OPTIMIZE-SPEED* 
+SB-PCL::*THE-CLASS-STRUCTURE-EFFECTIVE-SLOT-DEFINITION* 
+SB-PCL::*VAR-DECLARATIONS-WITHOUT-ARG* 
+SB-PCL::*THE-CLASS-INSTANCE* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-UNSIGNED-BYTE-4* 
+SB-PCL::*THE-CLASS-CONS* 
+SB-PCL::*THE-WRAPPER-OF-FIXNUM* 
+SB-PCL::*THE-SYSTEM-II-METHOD* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-SIGNED-BYTE-8* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-UNSIGNED-BYTE-16* 
+SB-PCL::*THE-WRAPPER-OF-COMPLEX-SINGLE-FLOAT* 
+SB-PCL::*THE-WRAPPER-OF-SINGLE-FLOAT* 
+SB-PCL::*DFUN-MISS-GFS-ON-STACK* 
+SB-PCL::*THE-CLASS-CONDITION-CLASS* 
+SB-PCL::*THE-WRAPPER-OF-BIGNUM* 
+SB-PCL::*THE-CLASS-SPECIALIZER-WITH-OBJECT* 
+SB-PCL::*THE-CLASS-COMPLEX* 
+SB-PCL::*THE-WRAPPER-OF-INTEGER* 
+SB-PCL::*CONDITION-SLOT-BOUNDP-USING-CLASS-METHOD* 
+SB-PCL::*THE-CLASS-CHARACTER* 
+SB-PCL::*EARLY-CLASS-DEFINITIONS* 
+SB-PCL::*THE-WRAPPER-OF-NULL* 
+SB-PCL::*INTERNAL-PCL-GENERALIZED-FUN-NAME-SYMBOLS* 
+SB-PCL::*THE-CLASS-STRUCTURE-CLASS* 
+SB-PCL::*UNSPECIFIC-ARG* 
+SB-PCL::*SECONDARY-DFUN-CALL-COST* 
+SB-PCL::*THE-CLASS-SIMPLE-STRING* 
+SB-PCL::*THE-CLASS-STRUCTURE-SLOT-DEFINITION* 
+SB-PCL::*METHOD-FUNCTION-PLIST* 
+SB-PCL::*STANDARD-METHOD-COMBINATION* 
+SB-PCL::*LAZY-DFUN-COMPUTE-P* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-SIGNED-BYTE-8* 
+SB-PCL::*THE-CLASS-STRING* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-UNSIGNED-BYTE-2* 
+SB-PCL::*EQL-SPECIALIZER-TABLE* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-COMPLEX-DOUBLE-FLOAT* 
+SB-PCL::*BUILT-IN-WRAPPER-SYMBOLS* 
+SB-PCL::*THE-CLASS-STREAM* 
+SB-PCL::*CLASS-EQ-SPECIALIZER-METHODS* 
+SB-PCL::*THE-CLASS-REAL* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-UNSIGNED-BYTE-32* 
+SB-PCL::*THE-CLASS-SIMPLE-VECTOR* 
+SB-PCL::*THE-CLASS-GENERIC-FUNCTION* 
+SB-PCL::*STANDARD-SETF-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*STD-CAM-METHODS* 
+SB-PCL::*THE-CLASS-STRUCTURE-DIRECT-SLOT-DEFINITION* 
+SB-PCL::*THE-WRAPPER-OF-FDEFN* 
+SB-PCL::*THE-WRAPPER-OF-VECTOR* 
+SB-PCL::*THE-CLASS-NULL* 
+SB-PCL::*NORMALIZE-TYPE 
+SB-PCL::*REBOUND-EFFECTIVE-METHOD-GENSYMS*
+SB-PCL::*THE-CLASS-NUMBER* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-STRING* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-SIGNED-BYTE-30* 
+SB-PCL::*MF2CP* 
+SB-PCL::*THE-CLASS-DEPENDENT-UPDATE-MIXIN* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-SIGNED-BYTE-32* 
+SB-PCL::*THE-CLASS-VECTOR* 
+SB-PCL::*FIND-CLASS* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-SIGNED-BYTE-16* 
+SB-PCL::*STANDARD-SLOT-LOCATIONS* 
+SB-PCL::*FGENS* 
+SB-PCL::*PCL-PACKAGE* 
+SB-PCL::*THE-CLASS-ARRAY* 
+SB-PCL::*CASE-TABLE-LIMIT* 
+SB-PCL::*THE-CLASS-METHOD-COMBINATION* 
+SB-PCL::*THE-CLASS-DIRECT-SLOT-DEFINITION* 
+SB-PCL::*THE-CLASS-FIXNUM* 
+SB-PCL::*THE-CLASS-STD-OBJECT* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-SIGNED-BYTE-32* 
+SB-PCL::*EARLY-CLASS-PREDICATES* 
+SB-PCL::*WRAPPER-OF-COST* 
+SB-PCL::*STANDARD-CLASSES* 
+SB-PCL::*THE-CLASS-LONG-METHOD-COMBINATION* 
+SB-PCL::*READERS-FOR-THIS-DEFCLASS*
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-SIGNED-BYTE-30* 
+SB-PCL::*EMF-CALL-TRACE-INDEX* 
+SB-PCL::*BUILT-IN-TYPEP-COST* 
+SB-PCL::*THE-CLASS-STANDARD-SLOT-DEFINITION* 
+SB-PCL::*THE-CLASS-PLIST-MIXIN* 
+SB-PCL::*CONDITION-SETF-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*THE-CLASS-BIGNUM* 
+SB-PCL::*THE-WRAPPER-OF-CODE-COMPONENT* 
+SB-PCL::*THE-ESLOTD-STANDARD-CLASS-SLOTS* 
+SB-PCL::*SLOT-NAMES-FOR-THIS-DEFCLASS*
+SB-PCL::*THE-CLASS-FUNCTION* 
+SB-PCL::*THE-WRAPPER-OF-SYSTEM-AREA-POINTER* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY* 
+SB-PCL::*THE-CLASS-STANDARD-EFFECTIVE-SLOT-DEFINITION* 
+SB-PCL::*THE-WRAPPER-OF-SEQUENCE* 
+SB-PCL::*THE-CLASS-FUNCALLABLE-STANDARD-CLASS* 
+SB-PCL::*THE-CLASS-SYSTEM-AREA-POINTER* 
+SB-PCL::*THE-WRAPPER-OF-LIST* 
+SB-PCL::*THE-CLASS-BUILT-IN-CLASS* 
+SB-PCL::*STRUCTURE-SETF-SLOT-VALUE-USING-CLASS-METHOD* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-UNSIGNED-BYTE-4* 
+SB-PCL::*THE-CLASS-SIMPLE-BIT-VECTOR* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-COMPLEX-SINGLE-FLOAT* 
+SB-PCL::*GLOBAL-EFFECTIVE-METHOD-GENSYMS* 
+SB-PCL::*DFUN-CONSTRUCTORS* 
+SB-PCL::*THE-CLASS-CODE-COMPONENT* 
+SB-PCL::*THE-CLASS-STANDARD-WRITER-METHOD* 
+SB-PCL::*NON-VAR-DECLARATIONS* 
+SB-PCL::*IN-PRECOMPUTE-EFFECTIVE-METHODS-P* 
+SB-PCL::*CHECKING-OR-CACHING-LIST* 
+SB-PCL::*THE-CLASS-BIT-VECTOR* 
+SB-PCL::*THE-WRAPPER-OF-CHARACTER* 
+SB-PCL::*THE-WRAPPER-OF-COMPLEX* 
+SB-PCL::*BUILT-IN-CLASSES* 
+SB-PCL::*ALLOW-EXPERIMENTAL-SPECIALIZERS-P* 
+SB-PCL::*ALLOW-EMF-CALL-TRACING-P* 
+SB-PCL::*SGF-METHOD-CLASS-INDEX* 
+SB-PCL::*SGF-METHODS-INDEX* 
+SB-PCL::*THE-WRAPPER-OF-NUMBER* 
+SB-PCL::*THE-WRAPPER-OF-RATIO* 
+SB-PCL::*RAISE-METATYPES-TO-CLASS-P* 
+SB-PCL::*EARLY-CLASS-SLOTS* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY* 
+SB-PCL::*MF1CP* 
+SB-PCL::*THE-WRAPPER-OF-RATIONAL* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-UNSIGNED-BYTE-16* 
+SB-PCL::*THE-WRAPPER-OF-ARRAY* 
+SB-PCL::*THE-ESLOTD-FUNCALLABLE-STANDARD-CLASS-SLOTS* 
+SB-PCL::*THE-CLASS-SLOT-CLASS* 
+SB-PCL::*THE-CLASS-SLOT-DEFINITION* 
+SB-PCL::*OPTIMIZE-CACHE-FUNCTIONS-P* 
+SB-PCL::*MF2* 
+SB-PCL::*THE-CLASS-SEQUENCE* 
+SB-PCL::*EQ-CASE-TABLE-LIMIT* 
+SB-PCL::*THE-CLASS-STRUCTURE-OBJECT* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-DOUBLE-FLOAT* 
+SB-PCL::*SLOT-VECTOR-SYMBOLS* 
+SB-PCL::*ENABLE-EMF-CALL-TRACING-P* 
+SB-PCL::*THE-CLASS-SINGLE-FLOAT* 
+SB-PCL::*THE-SYSTEM-SI-METHOD* 
+SB-PCL::*THE-WRAPPER-OF-BASE-CHAR* 
+SB-PCL::*THE-CLASS-CONDITION-SLOT-DEFINITION* 
+SB-PCL::*DFUN-LIST* 
+SB-PCL::*THE-CLASS-STANDARD-METHOD* 
+SB-PCL::*DFUN-ARG-SYMBOLS* 
+SB-PCL::*NOT-IN-CACHE* 
+SB-PCL::*THE-CLASS-STANDARD-BOUNDP-METHOD* 
+SB-PCL::*THE-CLASS-LIST* 
+SB-PCL::*NEW-CLASS* 
+SB-PCL::*THE-WRAPPER-OF-SIMPLE-ARRAY-UNSIGNED-BYTE-8* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-UNSIGNED-BYTE-2* 
+SB-PCL::*THE-CLASS-CLASS-EQ-SPECIALIZER* 
+SB-PCL::*THE-CLASS-FUNCALLABLE-INSTANCE* 
+SB-PCL::*IN-GF-ARG-INFO-P* 
+SB-PCL::*THE-CLASS-STANDARD-GENERIC-FUNCTION* 
+SB-PCL::*SGF-ARG-INFO-INDEX* 
+SB-PCL::*ALLOW-FORWARD-REFERENCED-CLASSES-IN-CPL-P* 
+SB-PCL::*THE-CLASS-FORWARD-REFERENCED-CLASS* 
+SB-PCL::*THE-CLASS-SIMPLE-ARRAY-SINGLE-FLOAT* 
+SB-PCL::*MF1P* 
+SB-PCL::*MINIMUM-CACHE-SIZE-TO-LIST*
+SB-PCL::*THE-CLASS-CLASS-PROTOTYPE-SPECIALIZER* 
+SB-PCL::*THE-CLASS-T* 
+SB-PCL::*CACHE-MISS-VALUES-STACK* 
+SB-PCL::*SGF-NAME-INDEX* 
+SB-PCL::*THE-CLASS-DEFINITION-SOURCE-MIXIN* 
+SB-PCL::*SGF-SLOTS-INIT* 
+
+
+= debugger
+
+*DEBUG-PRINT-LENGTH* 
+*TRACE-FRAME*
+*TRACE-ENCAPSULATE-DEFAULT* 
+*MAX-TRACE-INDENTATION* 
+*IN-THE-DEBUGGER* 
+*DEBUG-BEGINNER-HELP-P* 
+*STACK-TOP-HINT* 
+*DEBUG-HELP-STRING* 
+*ONLY-BLOCK-START-LOCATIONS* 
+*FLUSH-DEBUG-ERRORS* 
+*DEBUG-CONDITION*
+*TRACE-VALUES* 
+*PRINT-LOCATION-KIND* 
+*DEBUG-PRINT-LEVEL* 
+*TRACED-FUN-LIST*
+*TRACE-INDENTATION-STEP* 
+*DEBUG-READTABLE* 
+SB-DEBUG::*CACHED-DEBUG-SOURCE* 
+SB-DEBUG::*POSSIBLE-BREAKPOINTS*
+SB-DEBUG::*BAD-CODE-LOCATION-TYPES* 
+SB-DEBUG::*TRACED-ENTRIES* 
+SB-DEBUG::*DEBUG-COMMAND-LEVEL* 
+SB-DEBUG::*CACHED-SOURCE-STREAM* 
+SB-DEBUG::*DEBUG-LOOP-FUN* 
+SB-DEBUG::*NESTED-DEBUG-CONDITION*
+SB-DEBUG::*STEP-BREAKPOINTS* 
+SB-DEBUG::*DEBUG-RESTARTS*
+SB-DEBUG::*CACHED-FORM-NUMBER-TRANSLATIONS*
+SB-DEBUG::*BREAKPOINTS* 
+SB-DEBUG::*TRACED-FUNS* 
+SB-DEBUG::*DEBUG-COMMANDS* 
+SB-DEBUG::*DEFAULT-BREAKPOINT-DEBUG-FUN* 
+SB-DEBUG::*CACHED-READTABLE* 
+SB-DEBUG::*IN-TRACE* 
+SB-DEBUG::*CACHED-TOPLEVEL-FORM-OFFSET* 
+SB-DEBUG::*STACK-TOP* 
+SB-DEBUG::*CURRENT-FRAME* 
+SB-DEBUG::*CACHED-TOPLEVEL-FORM*
+SB-DEBUG::*NUMBER-OF-STEPS* 
+SB-DEBUG::*REAL-STACK-TOP* 
+SB-DI::*PARSING-BUFFER* 
+SB-DI::*IR1-BLOCK-DEBUG-BLOCK* 
+SB-DI::*OTHER-PARSING-BUFFER* 
+SB-DI::*COMPILED-DEBUG-FUNS* 
+SB-DI::*FORM-NUMBER-TEMP* 
+SB-DI::*COMPONENT-BREAKPOINT-OFFSETS* 
+SB-DI::*FUN-END-COOKIES* 
+SB-DI::*FORM-NUMBER-CIRCULARITY-TABLE* 
+SB-DI::*EXECUTING-BREAKPOINT-HOOKS* 
+SB-DI::*IR1-LAMBDA-DEBUG-FUN* 
+
+= profiler
+
+SB-PROFILE::*PROFILED-FUN-NAME->INFO* 
+SB-PROFILE::*ENCLOSED-CONSING* 
+SB-PROFILE::*COMPUTING-PROFILING-DATA-FOR*
+SB-PROFILE::*ENCLOSED-PROFILES* 
+SB-PROFILE::*TIMER-OVERHEAD-ITERATIONS* 
+SB-PROFILE::*OVERHEAD*
+SB-PROFILE::*ENCLOSED-TICKS* 
+
+= disassembler
+
+SB-DISASSEM:*DISASSEM-INST-ALIGNMENT-BYTES* 
+SB-DISASSEM:*DISASSEM-NOTE-COLUMN* 
+SB-DISASSEM:*DEFAULT-DSTATE-HOOKS* 
+SB-DISASSEM:*DISASSEM-OPCODE-COLUMN-WIDTH* 
+SB-DISASSEM:*DISASSEM-SCHEDULER-P*
+SB-DISASSEM:*DISASSEM-LOCATION-COLUMN-WIDTH* 
+SB-DISASSEM::*FOREIGN-SYMBOLS-BY-ADDR* 
+SB-DISASSEM::*ARG-FUN-PARAMS* 
+SB-DISASSEM::*ADDRESS-OF-NIL-OBJECT* 
+SB-DISASSEM::*DISASSEM-INSTS* 
+SB-DISASSEM::*DISASSEM-FUN-CACHE* 
+SB-DISASSEM::*DISASSEM-INST-FORMATS* 
+SB-DISASSEM::*GROKKED-SYMBOL-SLOTS* 
+SB-DISASSEM::*ARG-FORM-KINDS* 
+SB-DISASSEM::*DISASSEM-INST-SPACE* 
+SB-DISASSEM::*CURRENT-INSTRUCTION-FLAVOR* 
+SB-DISASSEM::*DISASSEM-ARG-TYPES* 
+SB-DISASSEM::*ASSEMBLER-ROUTINES-BY-ADDR* 
+
+= assembler
+
+Currently protected by *big-compiler-lock*.  Mostly uninvestigated
+
+SB-ASSEM:*ASSEM-MAX-LOCATIONS* 
+SB-ASSEM:*ASSEM-INSTRUCTIONS* 
+SB-ASSEM:*ASSEM-SCHEDULER-P* 
+SB-ASSEM::*INSTRUCTION-ATTRIBUTE-TRANSLATIONS* 
+SB-ASSEM::**CURRENT-VOP** 
+SB-ASSEM::**CURRENT-SEGMENT**
+
+
+= unix interface
+
+Looks good to me
+
+SB-UNIX::*INTERRUPTS-ENABLED*  ; ok,  bound
+SB-UNIX::*UNIX-SIGNALS*        ; ok, read-only
+SB-UNIX::*INTERRUPT-PENDING*  ; ok, bound
+
+= toplevel/environment stuff
+
+Some of these should probably be bound on thread entry.  I haven't
+checked them yet, except where indicated
+
+*DEBUG-IO* 
+SB-SYS:*TTY* 
+SB-SYS:*STDIN* 
+SB-SYS:*STDOUT* 
+SB-SYS:*STDERR* 
+SB-SYS:*LONG-SITE-NAME*    ; readonly
+SB-SYS:*SHORT-SITE-NAME*   ; readonly
+SB-SYS::*SOFTWARE-VERSION* ; readonly
+SB-THREAD::*SESSION-LOCK*  ; bound
+SB-THREAD::*BACKGROUND-THREADS-WAIT-FOR-DEBUGGER* ; intentionally global
+***   ; bound
+**    ; bound
+*     ; bound
+*PRINT-PRETTY* 
+*LOAD-VERBOSE* 
+*LOAD-TRUENAME* 
+*READ-BASE* 
+*BREAK-ON-SIGNALS* 
+*PRINT-READABLY* 
+*PRINT-CIRCLE* 
+*FEATURES*                     ; write at own risk
+*PRINT-BASE* 
+*PACKAGE* 
+*PRINT-RADIX* 
+*READ-SUPPRESS* 
+*ERROR-OUTPUT* 
+*DEFAULT-PATHNAME-DEFAULTS* 
+*LOAD-PATHNAME* 
+*STANDARD-OUTPUT* 
+*PRINT-RIGHT-MARGIN* 
+*READTABLE* 
+*PRINT-CASE* 
+*PRINT-MISER-WIDTH* 
+*PRINT-PPRINT-DISPATCH* 
+*PRINT-LENGTH* 
+*TERMINAL-IO* 
+*PRINT-GENSYM* 
+*QUERY-IO* 
+*STANDARD-INPUT* 
+*LOAD-PRINT* 
+*DEBUGGER-HOOK* 
+*PRINT-LINES* 
+*PRINT-ESCAPE* 
+*PRINT-LEVEL* 
+*ERROR-PRINT-LENGTH*
+*ERROR-PRINT-LINES*
+*READ-EVAL* 
+*PRINT-ESCAPE* 
+*TRACE-OUTPUT* 
+SB-IMPL::*INSPECT-LENGTH* 
+*ERROR-PRINT-LEVEL*
+SB-IMPL::*DRIBBLE-STREAM*         ; what to do with dribble in threaded lisp?
+SB-IMPL::*HELP-FOR-INSPECT*       ; read-only
+*PRINT-ARRAY*                     
+
+
+
+*POSIX-ARGV*     ; read-only
+*READ-DEFAULT-FLOAT-FORMAT*   ; probably "change at own risk"
+*MODULES*   ; should be changed only by provide/require, needs locking
+*MODULE-PROVIDER-FUNCTIONS* ; probably "change at own risk"
+SB-IMPL::*REPL-FUN*         
+SB-INT:*REPL-READ-FORM-FUN* 
+SB-INT:*REPL-PROMPT-FUN* 
+
+= the formatter & pretty printer
+
+== probably safe (readonly unless indicated)
+
+(defparameter *format-whitespace-chars*
+(defvar *format-directive-expanders*
+(defvar *format-directive-interpreters*
+(defvar *default-format-error-control-string* nil)
+(defvar *default-format-error-offset* nil)
+SB-FORMAT::*CARDINAL-ONES* ; readonly
+SB-FORMAT::*CARDINAL-TENS* 
+SB-FORMAT::*CARDINAL-TEENS* 
+SB-FORMAT::*ORDINAL-ONES* 
+SB-FORMAT::*ORDINAL-TENS* 
+SB-FORMAT::*CARDINAL-PERIODS* 
+SB-FORMAT::*ILLEGAL-INSIDE-JUSTIFICATION* 
+SB-FORMAT::*OUTSIDE-ARGS* ; safe, bound
+
+
+(defvar *up-up-and-out-allowed* nil) ; safe, bound
+(defvar *logical-block-popper* nil) ; bound
+(defvar *expander-next-arg-macro* 'expander-next-arg) ; bound
+(defvar *orig-args-available* nil) ; bound
+
+== needs checking
+
+;;; Used by the expander stuff. Initially starts as T, and gets set to NIL
+;;; if someone needs to do something strange with the arg list (like use
+;;; the rest, or something).  Setf in late-format, haven't checked if it's
+;;; always in a bound context
+(defvar *only-simple-args*)
+
+;;; setf in late-format, haven't checked
+(defvar *simple-args*)
+
+== haven't looked at yet
+
+SB-PRETTY::*INITIAL-PPRINT-DISPATCH* 
+SB-PRETTY::*BUILDING-INITIAL-TABLE* 
+SB-PRETTY::*PRECOMPILED-PPRINT-DISPATCH-FUNS* 
+
+
+= compiler
+
+Note that the compiler and fasloader are strongly believed not to be
+thread-safe, so there is currently a big lock (*big-compiler-lock*)
+around all calls to the compiler or fasloader
+
+*COMPILE-FILE-PATHNAME*  
+*COMPILE-FILE-TRUENAME* 
+*COMPILE-PRINT* 
+*COMPILE-VERBOSE* 
+*COMPILE-PROGRESS* 
+SB-C:*BACKEND-INTERNAL-ERRORS* 
+SB-C:*BACKEND-SPECIAL-ARG-TYPES* 
+SB-C:*ASSEMBLY-OPTIMIZE* 
+SB-C:*LEXENV*
+SB-C:*CODE-SEGMENT* 
+SB-C:*COMPONENT-BEING-COMPILED*
+SB-C:*BACKEND-SB-NAMES* 
+SB-C:*BACKEND-PAGE-SIZE* 
+SB-C:*BACKEND-SC-NUMBERS* 
+SB-C:*ELSEWHERE* 
+SB-C:*BACKEND-T-PRIMITIVE-TYPE* 
+SB-C:*BACKEND-SB-LIST* 
+SB-C:*SETF-ASSUMED-FBOUNDP* 
+SB-C:*FREE-FUNS*
+SB-C:*BACKEND-DISASSEM-PARAMS*
+SB-C:*BACKEND-SUBFEATURES* 
+SB-C:*COUNT-VOP-USAGES*
+SB-C:*SUPPRESS-VALUES-DECLARATION* 
+SB-C:*BACKEND-SC-NAMES* 
+SB-C::*CURRENT-COMPONENT*
+SB-C::*SLOT-INHERIT-ALIST* 
+SB-C::*COMPILER-NOTE-COUNT*
+SB-C::*BACKEND-PREDICATE-TYPES* 
+SB-C::*POLICY* 
+SB-C::*INLINEP-TRANSLATIONS* 
+SB-C::*PACK-OPTIMIZE-SAVES* 
+SB-C::*DELETION-IGNORED-OBJECTS* 
+SB-C::*LAST-FORMAT-ARGS* 
+SB-C::*CONSTANTS-BEING-CREATED* 
+SB-C::*IGNORED-ERRORS* 
+SB-C::*COMPILE-OBJECT* 
+SB-C::*TN-IDS* 
+SB-C::*LOSSAGE-FUN*
+SB-C::*COMPILER-ERROR-COUNT*
+SB-C::*EVENT-NOTE-THRESHOLD* 
+SB-C::*LAST-MESSAGE-COUNT* 
+SB-C::*EMIT-ASSEMBLY-CODE-NOT-VOPS-P* 
+SB-C::*LAST-FORMAT-STRING* 
+SB-C::*UNWINNAGE-FUN*
+SB-C::*COMPILER-ERROR-CONTEXT* 
+SB-C::*SEEN-BLOCKS* 
+SB-C::*TN-ID* 
+SB-C::*IR1-OPTIMIZE-UNTIL-DONE-EVENT-INFO* 
+SB-C::*SPECIALIZED-ARRAY-ELEMENT-TYPE-PROPERTIES* 
+SB-C::*NUMBER-CONTINUATIONS* 
+SB-C::*CTYPE-TEST-FUN*
+SB-C::*IGNORE-COST-VOPS* 
+SB-C::*QUEUED-PROCLAIMS*
+SB-C::*COMPILER-STYLE-WARNING-COUNT*
+SB-C::*LABEL-IDS* 
+SB-C::TN-NEXT* 
+SB-C::*BACKEND-SUPPORT-ROUTINES* 
+SB-C::*COMPILED-CODE-LOCATION-KINDS* 
+SB-C::*VOP-TN-REFS* 
+SB-C::*INFO-CACHE-VECTOR* 
+SB-C::*FAILURE-P*
+SB-C::*LAST-ORIGINAL-SOURCE*
+SB-C::NEXT*
+SB-C::LIST*-IR2-CONVERT-OPTIMIZER 
+SB-C::*COMPILER-ERROR-BAILOUT*
+SB-C::*DELAYED-IR1-TRANSFORMS*
+SB-C::*INFO-TYPES* 
+SB-C::*CACHED-INFO-ENVIRONMENT* 
+SB-C::*CHECK-CONSISTENCY* 
+SB-C::*SEEN-FUNS* 
+SB-C::*USING-VOP-TN-REFS* 
+SB-C::*MAKE-VALUE-CELL-EVENT-EVENT-INFO* 
+SB-C::*IN-PACK* 
+SB-C::*REPACK-BLOCK-EVENT-INFO* 
+SB-C::*UNPACK-TN-EVENT-INFO* 
+SB-C::*COALESCE-MORE-LTN-NUMBERS-EVENT-INFO* 
+SB-C::*IN-COMPILATION-UNIT* 
+SB-C::*BACKEND-TEMPLATE-NAMES* 
+SB-C::*BACKEND-PRIMITIVE-TYPE-NAMES* 
+SB-C::*CONSTRAINT-PROPAGATE* 
+SB-C::*BACKEND-PRIMITIVE-TYPE-ALIASES* 
+SB-C::*NO-COSTS* 
+SB-C::*PACK-ASSIGN-COSTS* 
+SB-C::*CURRENT-FORM-NUMBER*
+SB-C::*BACKEND-META-SC-NAMES* 
+SB-C::*BLOCK-COMPILE-ARG*
+SB-C::*COMPILER-ERROR-PRINT-LENGTH* 
+SB-C::*CONTINUATION-NUMBER* 
+SB-C::*PREV-SEGMENT*
+SB-C::*ALL-COMPONENTS*
+SB-C::*CONTROL-DELETED-BLOCK-EVENT-INFO* 
+SB-C::*ALLOW-DEBUG-CATCH-TAG* 
+SB-C::*ID-LABELS* 
+SB-C::*BACKEND-TYPE-PREDICATES* 
+SB-C::*COMPILER-WARNING-COUNT*
+SB-C::*SUPPRESS-NOTE-VOPS* 
+SB-C::*COMPILER-ERROR-PRINT-LEVEL* 
+SB-C::*COMPLEMENT-TYPE-CHECKS* 
+SB-C::*META-PRIMITIVE-TYPE-NAMES*
+SB-C::*FUN-NAMES-IN-THIS-FILE* 
+SB-C::*SPLIT-IR2-BLOCK-EVENT-INFO* 
+SB-C::*TRACE-TABLE*
+SB-C::*LAST-SOURCE-FORM*
+SB-C::*WEAKEN-TYPE-CACHE-VECTOR* 
+SB-C::*-DERIVE-TYPE-AUX 
+SB-C::*BLOCK-COMPILE*
+SB-C::*BIG-COMPILER-LOCK*                ; protecting the rest
+SB-C::*VM-SUPPORT-ROUTINES* 
+SB-C::*PRIMITIVE-TYPE-SLOT-ALIST* 
+SB-C::*PREVIOUS-LOCATION*
+SB-C::*BYTE-BUFFER*
+SB-C::*CONSTANTS-CREATED-SINCE-LAST-INIT* 
+SB-C::*LAST-SOURCE-CONTEXT*
+SB-C::*FIXUPS*
+SB-C::VOP* 
+SB-C::*REOPTIMIZE-MAXED-OUT-EVENT-INFO* 
+SB-C::*LIST-CONFLICTS-TABLE* 
+SB-C::*LOSSAGE-DETECTED*
+SB-C::*LAST-ERROR-CONTEXT* 
+SB-C::*PREV-VOP*
+SB-C::*POLICY-DEPENDENT-QUALITIES* 
+SB-C::*REOPTIMIZE-AFTER-TYPE-CHECK-MAX* 
+SB-C::*-DERIVE-TYPE-OPTIMIZER 
+SB-C::*IR1-ATTRIBUTE-TRANSLATIONS* 
+SB-C::*ARGS* 
+SB-C::*DYNAMIC-COUNTS-TN*
+SB-C::*EXTREME-NTHCDR-OPEN-CODE-LIMIT* 
+SB-C::*BACKEND-PARSED-VOPS* 
+SB-C::*ABORTED-COMPILATION-UNIT-COUNT*
+SB-C::*COPY-DELETED-MOVE-EVENT-INFO* 
+SB-C::*MAX-OPTIMIZE-ITERATIONS* 
+SB-C::*TOPLEVEL-LAMBDAS*
+SB-C::*SC-VOP-SLOTS* 
+SB-C::*IR1-OPTIMIZE-MAXED-OUT-EVENT-INFO* 
+SB-C::*CONTINUATION-NUMBERS* 
+SB-C::*WARNINGS-P*
+SB-C::*BACKEND-META-SC-NUMBERS* 
+SB-C::*FLAME-ON-NECESSARILY-UNDEFINED-FUNCTION* 
+SB-C::*UNPACK-FALLBACK-EVENT-INFO* 
+SB-C::*SOURCE-INFO* 
+SB-C::*LIVE-BLOCK*
+SB-C::*BACKEND-META-PRIMITIVE-TYPE-NAMES* 
+SB-C::*NEXT-LOCATION*
+SB-C::*ELSEWHERE-LABEL* 
+SB-C::*SOURCE-PATHS*
+SB-C::*CONSTANTS*
+SB-C::*BACKEND-META-SB-NAMES* 
+SB-C::*DEFAULT-NTHCDR-OPEN-CODE-LIMIT* 
+SB-C::*ALWAYS-OPTIMIZED-AWAY* 
+SB-C::*CURRENT-PATH*
+SB-C::*LABEL-ID* 
+SB-C::*ENTRY-POINTS* 
+SB-C::*COMPILER-TRACE-OUTPUT* 
+SB-C::*CONSTRAINT-NUMBER*
+SB-C::*INFO-CLASSES* 
+SB-C::*RESULT-FIXUPS*
+SB-C::*REPACK-BLOCKS*
+SB-C::IR1-CONVERT-LET* 
+SB-C::*CODE-VECTOR*
+SB-C::*FREE-VARS*
+SB-C::*SOURCE-CONTEXT-METHODS* 
+SB-C::*VOP-ATTRIBUTE-TRANSLATIONS* 
+SB-C::*COMPILER-ERROR-PRINT-LINES* 
+SB-C::*LIVE-VOP*
+SB-C::*POLICY-QUALITIES* 
+SB-C::*NO-LOADS* 
+SB-C::*TRACE-TABLE-INFO*
+SB-C::*UNDEFINED-WARNINGS*
+SB-C::*BLOCK-ATTRIBUTE-TRANSLATIONS* 
+SB-C::*EVENT-INFO* 
+SB-C::*BURP-ACTION* 
+SB-C::*UNWINNAGE-DETECTED*
+SB-C::*ID-TNS* 
+
+= fasloader
+
+SB-FASL:*STATIC-FOREIGN-SYMBOLS* 
+SB-FASL:*ASSEMBLER-ROUTINES* 
+SB-FASL:*FASL-FILE-TYPE* 
+SB-FASL::FOP-LIST*-4 
+SB-FASL::*COLD-LOAD-DUMP* 
+SB-FASL::FOP-LIST*-7 
+SB-FASL::FOP-LIST*-8 
+SB-FASL::FOP-LIST*-1 
+SB-FASL::FOP-LIST*-2 
+SB-FASL::*FASL-INPUT-STREAM*
+SB-FASL::*FOP-STACK* 
+SB-FASL::*LOAD-DEPTH* 
+SB-FASL::*FEATURES-AFFECTING-FASL-FORMAT* 
+SB-FASL::*DUMP-ONLY-VALID-STRUCTURES* 
+SB-FASL::*CURRENT-FOP-TABLE-SIZE*
+SB-FASL::*FOP-STACK-POINTER-ON-ENTRY*
+SB-FASL::*FREE-FOP-TABLES* 
+SB-FASL::*LOAD-SYMBOL-BUFFER* 
+SB-FASL::*CURRENT-CATCH-BLOCK* 
+SB-FASL::*FASL-HEADER-STRING-START-STRING* 
+SB-FASL::DUMP-FOP* 
+SB-FASL::FOP-LIST* 
+SB-FASL::*CIRCULARITIES-DETECTED*
+SB-FASL::*LOAD-CODE-VERBOSE* 
+SB-FASL::*FEATURES-POTENTIALLY-AFFECTING-FASL-FORMAT* 
+SB-FASL::*LOAD-SYMBOL-BUFFER-SIZE* 
+SB-FASL::*CURRENT-FOP-TABLE*
+SB-FASL::*FOP-STACK-POINTER* 
+SB-FASL::*CURRENT-FOP-TABLE-INDEX*
+SB-FASL::*FOP-FUNS* 
+SB-FASL::*CURRENT-UNWIND-PROTECT-BLOCK* 
+SB-FASL::FOP-LIST*-5 
+SB-FASL::*FOP-NAMES* 
+SB-FASL::FOP-LIST*-6 
+SB-FASL::FOP-LIST*-3 
+
+
+= runtime stuff
+SB-VM:*STATIC-SPACE-FREE-POINTER* 
+SB-VM:*INITIAL-DYNAMIC-SPACE-FREE-POINTER* 
+SB-VM:*CURRENT-CATCH-BLOCK* 
+SB-VM:*STATIC-SYMBOLS* 
+SB-VM:*CONTROL-STACK-START* ; bound at thread entry
+SB-VM:*READ-ONLY-SPACE-FREE-POINTER* 
+SB-VM:*BINDING-STACK-START* 
+SB-VM:*CONTROL-STACK-END* 
+SB-VM::*CURRENT-UNWIND-PROTECT-BLOCK* 
+SB-VM::*FREE-TLS-INDEX* 
+SB-VM::*BINDING-STACK-POINTER* 
+SB-VM::*ALLOCATION-POINTER*    ; may be mostly unused ?
+SB-KERNEL:*PSEUDO-ATOMIC-ATOMIC*  ; bound
+SB-KERNEL:*CONTROL-STACK-EXHAUSTION-SAP*
+SB-KERNEL:*FREE-INTERRUPT-CONTEXT-INDEX*   ; bound
+SB-KERNEL:*GC-INHIBIT*  ; do not touch directly.  accessors may be broke too
+SB-KERNEL:*NEED-TO-COLLECT-GARBAGE* 
+SB-KERNEL:*ALREADY-MAYBE-GCING*
+SB-KERNEL:*PSEUDO-ATOMIC-INTERRUPTED* ; bound
+SB-KERNEL::*GC-TRIGGER*  ; I think this is dead, check
+SB-IMPL::*CURRENT-UNWIND-PROTECT-BLOCK*
+SB-IMPL::*CURRENT-CATCH-BLOCK*
+SB-IMPL::*READ-ONLY-SPACE-FREE-POINTER*
+SB-VM::*ALIEN-STACK* 
+
+*GC-NOTIFY-STREAM*   ; going away
+*BEFORE-GC-HOOKS*    ; must be global
+*AFTER-GC-HOOKS*     ; ditto
+*GC-NOTIFY-AFTER*    ; going away
+*GC-NOTIFY-BEFORE*   ; going away
+*GC-RUN-TIME*        
+SB-INT:*N-BYTES-FREED-OR-PURIFIED* 
+
+Note also the following may need attention
+SB-PROFILE::TOTAL-CONSED
+GET-BYTES-CONSED (fbound)
+BYTES-CONSED-BETWEEN-GCS (fbound)
+
+
+= backend constants 
+
+These are believed to be constant (in the general sense, not necessarily 
+eligible for defconstant).  Mostly they're attributes of the backend machine
+
+
+SB-C:*BACKEND-REGISTER-SAVE-PENALTY* 
+SB-C:*BACKEND-BYTE-ORDER* 
+SB-C:*BACKEND-INSTRUCTION-FORMATS* 
+SB-C:*BACKEND-INSTRUCTION-FLAVORS* 
+SB-VM:*PRIMITIVE-OBJECTS* 
+SB-VM:*ASSEMBLY-UNIT-LENGTH* 
+SB-VM::*FP-CONSTANT-L2E*
+SB-VM::*FLOAT-REGISTER-NAMES* 
+SB-VM::*FP-CONSTANT-1L0*
+SB-VM::*BYTE-REGISTER-NAMES* 
+SB-VM::*DWORD-SC-NAMES* 
+SB-VM::*FP-CONSTANT-1F0* 
+SB-VM::*WORD-REGS* 
+SB-VM::*BYTE-SC-NAMES* 
+SB-VM::*DEFAULT-ADDRESS-SIZE* 
+SB-VM::*FP-CONSTANT-0D0* 
+SB-VM::*FP-CONSTANT-LG2*
+SB-VM::*FP-CONSTANT-L2T*
+SB-VM::*DWORD-REGS* 
+SB-VM::*WORD-REGISTER-NAMES* 
+SB-VM::*FP-CONSTANT-0F0* 
+SB-VM::*FLOAT-SC-NAMES* 
+SB-VM::*FLOAT-REGS* 
+SB-VM::*DWORD-REGISTER-NAMES* 
+SB-VM::*WORD-SC-NAMES* 
+SB-VM::*FP-CONSTANT-PI*
+SB-VM::*BYTE-REGS* 
+SB-VM::*FP-CONSTANT-1D0* 
+SB-VM::*DOUBLE-SC-NAMES* 
+SB-VM::*FP-CONSTANT-0L0*
+SB-VM::*REGISTER-ARG-OFFSETS* 
+SB-VM::*FLOAT-TRAP-ALIST* 
+SB-VM::*DWORD-REG-NAMES* 
+SB-VM::*BYTE-REG-NAMES* 
+SB-VM::*ROUNDING-MODE-ALIST* 
+SB-VM::*REGISTER-ARG-NAMES* 
+SB-VM::*FUN-HEADER-WIDETAGS* 
+SB-VM::*FIXNUM-PRIMITIVE-TYPE* 
+SB-VM:*STATIC-FUNS* 
+SB-VM::*FP-CONSTANT-LN2*
+SB-VM::*WORD-REG-NAMES* 
+SB-KERNEL::*BUILT-IN-CLASSES* 
+
+= dead stuff
+
+SB-SYS:*TASK-NOTIFY*    ; unused
+SB-SYS:*TASK-SELF*      ; unused
+SB-SYS:*TASK-DATA*      ; unused
+SB-SYS:*TYPESCRIPTPORT* ' unused
+SB-THREAD::*FOREGROUND-THREAD-STACK*    ; unused, I think
+
+
+
+
+------------------------------------------------------------------------
+
+= unclassified
+
+SB-ALIEN-INTERNALS:*VALUES-TYPE-OKAY* 
+SB-ALIEN::ALIEN-*-TYPE-TRANSLATOR 
+SB-ALIEN::*DSO-LINKER* 
+SB-ALIEN::*HANDLES-FROM-DLOPEN* 
+SB-ALIEN::*ALIEN-TYPE-CLASSES* 
+SB-ALIEN::*RECORD-TYPES-ALREADY-UNPARSED*
+SB-ALIEN::*NEW-AUXILIARY-TYPES* 
+SB-ALIEN::*DSO-LINKER-OPTIONS* 
+SB-ALIEN::*METHOD-SLOT-ALIST* 
+
+SB-VM::*SIGNED-IMM-BYTE-PREFILTER-WRAPPER* 
+SB-VM::*DISPLACEMENT-PRINTER-WRAPPER* 
+SB-VM::*ACCUM-PRINTER-WRAPPER* 
+SB-VM::*WIDTH-PRINTER-WRAPPER* 
+SB-VM::*LABEL-1-PREFILTER-WRAPPER* 
+SB-VM::*WORD-ACCUM-PRINTER-WRAPPER* 
+SB-VM::*NUM-FIXUPS* 
+SB-VM::*SIGNED-IMM-DATA-PREFILTER-WRAPPER* 
+SB-VM::*FIXNUM-PRIMITIVE-TYPE* 
+SB-VM::*CONDITIONS* 
+SB-VM::*IGNORE-AFTER* 
+SB-VM::*IMM-WORD-PREFILTER-WRAPPER* 
+SB-VM::*REGISTER-ARG-TNS* 
+SB-VM::*IMM-DATA-PREFILTER-WRAPPER* 
+SB-VM::*PRIMITIVE-TYPE-AUX-CACHE-VECTOR* 
+SB-VM::*LABEL-2-PREFILTER-WRAPPER* 
+SB-VM::*ROOM-INFO* 
+SB-VM::*ADJUSTABLE-VECTORS* 
+SB-VM::*CONDITION-NAME-VEC* 
+SB-VM::*IMM-WORD-16-PREFILTER-WRAPPER* 
+SB-VM::*SIMPLE-ARRAY-PRIMITIVE-TYPES* 
+SB-VM::*MAYBE-USE-INLINE-ALLOCATION* 
+SB-VM::*SIGNED-IMM-DWORD-PREFILTER-WRAPPER* 
+SB-VM::*IMMEDIATE-TYPES* 
+
+SB-KERNEL:*WILD-TYPE* 
+SB-KERNEL:*UNPARSE-FUN-TYPE-SIMPLIFY* 
+SB-KERNEL:*CURRENT-LEVEL-IN-PRINT* 
+SB-KERNEL:*UNIVERSAL-FUN-TYPE* 
+SB-KERNEL:*COLD-INIT-COMPLETE-P* 
+SB-KERNEL:*UNIVERSAL-TYPE* 
+SB-KERNEL:*HANDLER-CLUSTERS* 
+SB-KERNEL:*EMPTY-TYPE* 
+SB-KERNEL:*MAXIMUM-ERROR-DEPTH* 
+SB-KERNEL:*CONDITION-RESTARTS* 
+SB-KERNEL:*TYPE-SYSTEM-INITIALIZED* 
+SB-KERNEL:*RESTART-CLUSTERS* 
+SB-KERNEL::*MAKE-VALUES-TYPE-CACHED-CACHE-VECTOR* 
+SB-KERNEL::*BUILT-IN-CLASS-CODES* 
+SB-KERNEL::*DEF!STRUCT-TYPE-MAKE-LOAD-FORM-FUN* 
+SB-KERNEL::*LAYOUT-CLOS-HASH-RANDOM-STATE* 
+SB-KERNEL::*TYPE-TEST-ORDERING* 
+SB-KERNEL::*COMMON-TYPESPECS* 
+SB-KERNEL::*TYPE=-CACHE-VECTOR* 
+SB-KERNEL::*VALUES-SUBTYPEP-CACHE-VECTOR* 
+SB-KERNEL::*TYPECHECKFUNS* 
+SB-KERNEL::*%TYPE-INTERSECTION-CACHE-VECTOR* 
+SB-KERNEL::*TYPE-INTERSECTION2-CACHE-VECTOR* 
+SB-KERNEL::*COLD-INIT-FORMS*
+SB-KERNEL::*DEFAULT-DEFAULT* 
+SB-KERNEL::*RAW-SLOT-DATA-LIST* 
+SB-KERNEL::*SPECIALIZED-ARRAY-ELEMENT-TYPES* 
+SB-KERNEL::*DEFSTRUCT-HOOKS* 
+SB-KERNEL::*VALUES-TYPE-UNION-CACHE-VECTOR* 
+SB-KERNEL::*INTERNAL-ERRORS* 
+SB-KERNEL::*VALUES-TYPE-INTERSECTION-CACHE-VECTOR* 
+SB-KERNEL::*FORWARD-REFERENCED-LAYOUTS* 
+SB-KERNEL::*SYSTEM-LETS* 
+SB-KERNEL::*%COERCE-TO-VALUES-CACHE-VECTOR* 
+SB-KERNEL::*IGNORABLE-VARS*
+SB-KERNEL::*ENV-VAR* 
+SB-KERNEL::|*%%MAKE-UNION-TYPE-cached-CACHE-VECTOR*| 
+SB-KERNEL::*CSUBTYPEP-CACHE-VECTOR* 
+SB-KERNEL::*EMPTY-CONDITION-SLOT* 
+SB-KERNEL::*TYPE-UNION2-CACHE-VECTOR* 
+SB-KERNEL::*TYPE-CLASS-FUN-SLOTS* 
+SB-KERNEL::*ARG-TESTS* 
+SB-KERNEL::*USER-LETS* 
+SB-KERNEL::|*%%MAKE-ARRAY-TYPE-cached-CACHE-VECTOR*| 
+SB-KERNEL::*FINDING-NAME* 
+SB-KERNEL::*TYPE-CLASSES* 
+SB-KERNEL::*VALUES-SPECIFIER-TYPE-CACHE-VECTOR* 
+SB-KERNEL::*FLOAT-FORMATS* 
+SB-KERNEL::*INTERNAL-ERROR-ARGS*
+SB-KERNEL::*DEF!STRUCT-SUPERTYPE* 
+SB-KERNEL::*%TYPE-UNION-CACHE-VECTOR* 
+SB-KERNEL::*CTYPE-OF-CACHE-VECTOR* 
+
+SB-IMPL::*READ-BUFFER* 
+SB-IMPL::*SECONDARY-ATTRIBUTE-TABLE* 
+SB-IMPL::*STANDARD-READTABLE* 
+SB-IMPL::*OUCH-PTR* 
+SB-IMPL::*ERROR-ERROR-DEPTH* 
+SB-IMPL::*CURRENT-ERROR-DEPTH* 
+SB-IMPL::*INTERNAL-REAL-TIME-BASE-SECONDS* 
+SB-IMPL::*DAYLIGHT-TABLE* 
+SB-IMPL::*OFFENDING-DATUM*
+SB-IMPL::*HANDLERS-INSTALLED* 
+SB-IMPL::*READ-FROM-STRING-SPARES* 
+SB-IMPL::*HASH-TABLE-TESTS* 
+SB-IMPL::*ATTRIBUTE-NAMES* 
+SB-IMPL::*DAYS-BEFORE-MONTH* 
+SB-IMPL::*CHARACTER-ATTRIBUTES* 
+SB-IMPL::*UNIX-HOST* 
+SB-IMPL::*DESCRIPTOR-HANDLERS* 
+SB-IMPL::*STRING-OUTPUT-STREAMS* 
+SB-IMPL::*CLOSE-ON-ERROR* 
+SB-IMPL::*INTEGER-READER-SAFE-DIGITS* 
+SB-IMPL::*TIMEZONE-TABLE* 
+SB-IMPL::*BQ-COMMA-FLAG* 
+SB-IMPL::*PRINT-OBJECT-IS-DISABLED-P*
+SB-IMPL::*MERGE-SORT-TEMP-VECTOR* 
+SB-IMPL::*PROFILE-HASH-CACHE* 
+SB-IMPL::*FIXNUM-POWER--1* 
+SB-IMPL::*OBJECTS-PENDING-FINALIZATION* 
+SB-IMPL::*SHARP-EQUAL-CIRCLE-TABLE*
+SB-IMPL::*SOFTWARE-INTERRUPT-VECTOR*        ; suspect unused
+SB-IMPL::*INSPECT-UNBOUND-OBJECT-MARKER* 
+SB-IMPL::*IN-PACKAGE-INIT* 
+SB-IMPL::*DELAYED-DEF!METHOD-ARGS*
+SB-IMPL::*GENTEMP-COUNTER* 
+SB-IMPL::*CLOSE-IN-PARENT* 
+SB-IMPL::*IN-COMPILATION-UNIT*
+SB-IMPL::*CIRCULARITY-HASH-TABLE* 
+SB-IMPL::*LOAD-PRINT-STUFF*
+SB-IMPL::*ZAP-ARRAY-DATA-TEMP* 
+SB-IMPL::*ACTIVE-PROCESSES* 
+SB-IMPL::*SHARP-SHARP-ALIST*     
+SB-IMPL::*BASE-POWER* 
+SB-IMPL::*LOGICAL-PATHNAME-DEFAULTS* 
+SB-IMPL::*AVAILABLE-BUFFERS* 
+SB-IMPL::*BQ-DOT-FLAG* 
+SB-IMPL::*CIRCULARITY-COUNTER* 
+SB-IMPL::*DIGITS* 
+SB-IMPL::*PREVIOUS-READTABLE-CASE* 
+SB-IMPL::*BQ-VECTOR-FLAG* 
+SB-IMPL::*ABBREV-WEEKDAY-TABLE* 
+SB-IMPL::*LOGICAL-HOSTS* 
+SB-IMPL::*PACKAGE-NAMES* 
+SB-IMPL::*INSPECT-FUN* 
+SB-IMPL::*ABBREV-MONTH-TABLE* 
+SB-IMPL::*OUTPUT-ROUTINES* 
+SB-IMPL::*CHAR-NAME-ALIST* 
+SB-IMPL::*VALID-FUN-NAMES-ALIST* 
+SB-IMPL::*PERIODIC-POLLING-FUNCTION* 
+SB-IMPL::*ABORTED-COMPILATION-UNIT-COUNT*
+SB-IMPL::*LONG-WEEKDAY-TABLE* 
+SB-IMPL::*INTERNAL-SYMBOL-OUTPUT-FUN* 
+SB-IMPL::*BACKQUOTE-COUNT* 
+SB-IMPL::*DIGIT-BASES* 
+SB-IMPL::*PREVIOUS-DRIBBLE-STREAMS* 
+SB-IMPL::*MAX-EVENT-TO-USEC* 
+SB-IMPL::*INPUT-ROUTINES* 
+SB-IMPL::*MAX-EVENT-TO-SEC* 
+SB-IMPL::*READ-BUFFER-LENGTH* 
+SB-IMPL::*LONG-MONTH-TABLE* 
+SB-IMPL::*OLD-PACKAGE* 
+SB-IMPL::*INTEGER-READER-BASE-POWER* 
+SB-IMPL::*ERROR-THROW-UP-COUNT* 
+SB-IMPL::*BQ-AT-FLAG* 
+SB-IMPL::*MACHINE-VERSION*   ; unset/unbound ?  are we using this?
+SB-IMPL::*IGNORE-WILDCARDS* 
+SB-IMPL::*INCH-PTR* 
+SB-IMPL::*SHARP-EQUAL-ALIST* 
+SB-IMPL::*PREVIOUS-CASE* 
+
+*INLINE-EXPANSION-LIMIT* 
+*DERIVE-FUNCTION-TYPES* 
+*ENCLOSING-SOURCE-CUTOFF* 
+*INSPECTED*
+*UNDEFINED-WARNING-LIMIT* 
+*EFFICIENCY-NOTE-COST-THRESHOLD* 
+*EFFICIENCY-NOTE-LIMIT* 
+*USE-IMPLEMENTATION-TYPES* 
+*INTEXP-MAXIMUM-EXPONENT* 
+*GENSYM-COUNTER* 
+*MACROEXPAND-HOOK* 
+*RANDOM-STATE* 
+
+SB-BIGNUM::*TRUNCATE-Y*
+SB-BIGNUM::*TRUNCATE-X*
+
+SB-INT:*CL-PACKAGE* 
+SB-INT:*KEYWORD-PACKAGE* 
+SB-INT:*SETF-FDEFINITION-HOOK* 
+SB-INT:*DEFAULT-INIT-CHAR-FORM* 
+SB-INT:*EOF-OBJECT* 
+SB-INT:*AFTER-SAVE-INITIALIZATIONS* 
+SB-INT:*LOAD-SOURCE-DEFAULT-TYPE* 
+SB-INT:*BEFORE-SAVE-INITIALIZATIONS* 
+SB-INT:*INFO-ENVIRONMENT* 
index f3daccc..dcff492 100644 (file)
@@ -204,9 +204,14 @@ and submit it as a patch."
   (#!+gencgc last-gen #!-gencgc ignore sb!alien:int))
 
 #!+sb-thread
-(def-c-var-frob gc-thread-pid "gc_thread_pid")
+(progn
+  (sb!alien:define-alien-routine gc-stop-the-world sb!alien:void)
+  (sb!alien:define-alien-routine gc-start-the-world sb!alien:void))
+#!-sb-thread
+(progn
+  (defun gc-stop-the-world ())
+  (defun gc-start-the-world ()))
 
-       
 \f
 ;;;; SUB-GC
 
@@ -220,7 +225,8 @@ and submit it as a patch."
 
 ;;; SUB-GC does a garbage collection.  This is called from three places:
 ;;; (1) The C runtime will call here when it detects that we've consed 
-;;;     enough to exceed the gc trigger threshold
+;;;     enough to exceed the gc trigger threshold.  This is done in
+;;;     alloc() for gencgc or interrupt_maybe_gc() for cheneygc
 ;;; (2) The user may request a collection using GC, below
 ;;; (3) At the end of a WITHOUT-GCING section, we are called if
 ;;;     *NEED-TO-COLLECT-GARBAGE* is true
@@ -232,37 +238,25 @@ and submit it as a patch."
 
 ;;; For GENCGC all generations < GEN will be GC'ed.
 
-#!+sb-thread
-(defun sub-gc (&key (gen 0) &aux (pre-gc-dynamic-usage (dynamic-usage)))
-  (setf *need-to-collect-garbage* t)
-  (when (zerop *gc-inhibit*)
-    (setf (sb!alien:extern-alien "maybe_gc_pending" (sb!alien:unsigned 32))
-         (1+ gen))
-    (if (zerop (sb!alien:extern-alien "stop_the_world" (sb!alien:unsigned 32)))
-       (sb!unix:unix-kill (gc-thread-pid) :SIGALRM))
-    (loop
-     (when (zerop
-           (sb!alien:extern-alien "maybe_gc_pending" (sb!alien:unsigned 32)))
-       (return nil)))
-    (incf *n-bytes-freed-or-purified*
-         (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
-    (setf *need-to-collect-garbage* nil)
-    (scrub-control-stack))
-  (values))
-
-#!-sb-thread
 (defvar *already-in-gc* nil "System is running SUB-GC")
-#!-sb-thread
+(defvar *gc-mutex* (sb!thread:make-mutex :name "GC Mutex"))
+
 (defun sub-gc (&key (gen 0) &aux (pre-gc-dynamic-usage (dynamic-usage)))
   (when *already-in-gc* (return-from sub-gc nil))
   (setf *need-to-collect-garbage* t)
   (when (zerop *gc-inhibit*)
-    (let ((*already-in-gc* t))
-      (without-interrupts (collect-garbage gen))
-      (incf *n-bytes-freed-or-purified*
-           (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
-      (setf *need-to-collect-garbage* nil))
-    (scrub-control-stack))
+    (sb!thread:with-recursive-lock (*gc-mutex*)
+      (let ((*already-in-gc* t))
+       (without-interrupts
+        (gc-stop-the-world)
+        ;; XXX run before-gc-hooks
+        (collect-garbage gen)
+        (incf *n-bytes-freed-or-purified*
+              (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
+        (setf *need-to-collect-garbage* nil)
+        ;; XXX run after-gc-hooks
+        (gc-start-the-world)))
+      (scrub-control-stack)))
   (values))
        
 
index 9893bd8..d55ff8f 100644 (file)
@@ -125,8 +125,7 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
      (setf (waitqueue-data queue)
           (delete pid (waitqueue-data queue))))))
 
-;;; this should probably only be called while holding the queue spinlock.
-;;; not sure
+;;; 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  :sigcont))))
index 20f86a4..4402723 100644 (file)
@@ -369,11 +369,6 @@ void arch_install_interrupt_handlers()
     undoably_install_low_level_interrupt_handler(SIGTRAP, sigtrap_handler);
 }
 
-void get_spinlock(lispobj *word, int value) {
-    /* FIXME: dummy definition */
-    *word = value;
-}
-
 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
 
 lispobj funcall0(lispobj function)
index 16aac2b..28008db 100644 (file)
@@ -1,6 +1,19 @@
 #ifndef _ALPHA_ARCH_H
 #define _ALPHA_ARCH_H
 
+
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    *word=value;               /* FIXME for threads */
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
 #define ARCH_HAS_FLOAT_REGISTERS
 
 #endif /* _ALPHA_ARCH_H */
index fe8e540..ae5f1f9 100644 (file)
@@ -271,6 +271,8 @@ backtrace(int nframes)
 
 #else
 
+
+
 void
 backtrace(int nframes)
 {
index c0a1eeb..4760349 100644 (file)
@@ -19,6 +19,8 @@
 extern void gc_init(void);
 extern void gc_initialize_pointers(void);
 extern void collect_garbage(unsigned last_gen);
+extern void gc_init_tables(void);
+
 
 #include "os.h"
 
index f402282..e257afb 100644 (file)
@@ -80,7 +80,6 @@ struct page {
 extern struct page page_table[NUM_PAGES];
 
 \f
-void  gencgc_pickup_dynamic(void);
 
 void sniff_code_object(struct code *code, unsigned displacement);
 void gencgc_apply_code_fixups(struct code *old_code, struct code *new_code);
index 1afe504..08e3bc4 100644 (file)
 /* assembly language stub that executes trap_PendingInterrupt */
 void do_pending_interrupt(void);
 
+/* forward declarations */
+int gc_find_freeish_pages(int *restart_page_ptr, int nbytes, int unboxed, struct alloc_region *alloc_region);
+void  gc_set_region_empty(struct alloc_region *region);
+void gc_alloc_update_all_page_tables(void);
+static void  gencgc_pickup_dynamic(void);
+boolean interrupt_maybe_gc_int(int, siginfo_t *, void *);
+
 \f
 /*
  * GC parameters
@@ -337,6 +344,8 @@ gen_av_mem_age(int gen)
        / ((double)generations[gen].bytes_allocated);
 }
 
+void fpu_save(int *);          /* defined in x86-assem.S */
+void fpu_restore(int *);       /* defined in x86-assem.S */
 /* The verbose argument controls how much to print: 0 for normal
  * level of detail; 1 for debugging. */
 static void
@@ -506,7 +515,7 @@ gc_alloc_new_region(int nbytes, int unboxed, struct alloc_region *alloc_region)
     gc_assert((alloc_region->first_page == 0)
              && (alloc_region->last_page == -1)
              && (alloc_region->free_pointer == alloc_region->end_addr));
-    get_spinlock(&free_pages_lock,alloc_region);
+    get_spinlock(&free_pages_lock,(int) alloc_region);
     if (unboxed) {
        first_page =
            generations[gc_alloc_generation].alloc_unboxed_start_page;
@@ -568,7 +577,7 @@ gc_alloc_new_region(int nbytes, int unboxed, struct alloc_region *alloc_region)
                       (lispobj)(((char *)heap_base) + last_free_page*4096),
                       0);
     }
-    free_pages_lock=0;
+    release_spinlock(&free_pages_lock);
     
     /* we can do this after releasing free_pages_lock */
     if (gencgc_zero_check) {
@@ -710,7 +719,7 @@ gc_alloc_update_page_tables(int unboxed, struct alloc_region *alloc_region)
 
     next_page = first_page+1;
 
-    get_spinlock(&free_pages_lock,alloc_region);
+    get_spinlock(&free_pages_lock,(int) alloc_region);
     if (alloc_region->free_pointer != alloc_region->start_addr) {
        /* some bytes were allocated in the region */
        orig_first_page_bytes_used = page_table[first_page].bytes_used;
@@ -814,7 +823,7 @@ gc_alloc_update_page_tables(int unboxed, struct alloc_region *alloc_region)
        page_table[next_page].allocated = FREE_PAGE;
        next_page++;
     }
-    free_pages_lock=0;
+    release_spinlock(&free_pages_lock);
     /* alloc_region is per-thread, we're ok to do this unlocked */
     gc_set_region_empty(alloc_region);
 }
@@ -857,7 +866,7 @@ gc_alloc_large(int nbytes, int unboxed, struct alloc_region *alloc_region)
        index ahead of the current region and bumped up here to save a
        lot of re-scanning. */
 
-    get_spinlock(&free_pages_lock,alloc_region);
+    get_spinlock(&free_pages_lock,(int) alloc_region);
 
     if (unboxed) {
        first_page =
@@ -957,7 +966,7 @@ gc_alloc_large(int nbytes, int unboxed, struct alloc_region *alloc_region)
        SetSymbolValue(ALLOCATION_POINTER,
                       (lispobj)(((char *)heap_base) + last_free_page*4096),0);
     }
-    free_pages_lock=0;
+    release_spinlock(&free_pages_lock);
 
     return((void *)(page_address(first_page)+orig_first_page_bytes_used));
 }
@@ -3661,28 +3670,14 @@ garbage_collect_generation(int generation, int raise)
     /* Scavenge the stacks' conservative roots. */
     for_each_thread(th) {
        void **ptr;
+       void **esp= (void **) &raise;
 #ifdef LISP_FEATURE_SB_THREAD
-       struct user_regs_struct regs;
-       if(ptrace(PTRACE_GETREGS,th->pid,0,&regs)){
-           /* probably doesn't exist any more. */
-           fprintf(stderr,"child pid %d, %s\n",th->pid,strerror(errno));
-           perror("PTRACE_GETREGS");
+       if(th!=arch_os_get_current_thread()) {
+           os_context_t *last_context=get_interrupt_context_for_thread(th);
+           esp = (void **)*os_context_register_addr(last_context,reg_ESP);
        }
-       preserve_pointer(regs.ebx);
-       preserve_pointer(regs.ecx);
-       preserve_pointer(regs.edx);
-       preserve_pointer(regs.esi);
-       preserve_pointer(regs.edi);
-       preserve_pointer(regs.ebp);
-       preserve_pointer(regs.eax);
-#endif
-       for (ptr = th->control_stack_end;
-#ifdef LISP_FEATURE_SB_THREAD
-            ptr > regs.esp;
-#else
-            ptr > (void **)&raise;
 #endif
-            ptr--) {
+       for (ptr = (void **)th->control_stack_end; ptr > esp;  ptr--) {
            preserve_pointer(*ptr);
        }
     }
@@ -3985,7 +3980,7 @@ collect_garbage(unsigned last_gen)
     update_x86_dynamic_space_free_pointer();
     auto_gc_trigger = bytes_allocated + bytes_consed_between_gcs;
     if(gencgc_verbose)
-       fprintf(stderr,"Next gc when %d bytes have been consed\n",
+       fprintf(stderr,"Next gc when %ld bytes have been consed\n",
                auto_gc_trigger);
     SHOW("returning from collect_garbage");
 }
@@ -4167,7 +4162,6 @@ gc_initialize_pointers(void)
 
 \f
 
-extern boolean maybe_gc_pending ;
 /* alloc(..) is the external interface for memory allocation. It
  * allocates to generation 0. It is not called from within the garbage
  * collector as it is only external uses that need the check for heap
@@ -4204,7 +4198,7 @@ alloc(int nbytes)
            __asm__("movl %fs,%0" : "=r" (fs)  : );
            fprintf(stderr, "fs is %x, th->tls_cookie=%x (should be identical)\n",
                    debug_get_fs(),th->tls_cookie);
-           lose("If you see this message before 2003.05.01, mail details to sbcl-devel\n");
+           lose("If you see this message before 2003.12.01, mail details to sbcl-devel\n");
        }
 #else
     gc_assert(SymbolValue(PSEUDO_ATOMIC_ATOMIC,th));
@@ -4224,8 +4218,8 @@ alloc(int nbytes)
     if (auto_gc_trigger && bytes_allocated > auto_gc_trigger) {
        /* set things up so that GC happens when we finish the PA
         * section.  */
-       maybe_gc_pending=1;
-       SetSymbolValue(PSEUDO_ATOMIC_INTERRUPTED, make_fixnum(1),th);
+       struct interrupt_data *data=th->interrupt_data;
+       maybe_defer_handler(interrupt_maybe_gc_int,data,0,0,0);
     }
     new_obj = gc_alloc_with_region(nbytes,0,region,0);
     return (new_obj);
@@ -4306,10 +4300,9 @@ gencgc_handle_wp_violation(void* fault_addr)
             */
            if(page_table[page_index].write_protected_cleared != 1) 
                lose("fault in heap page not marked as write-protected");
-           
-           /* Don't worry, we can handle it. */
-           return 1;
        }
+       /* Don't worry, we can handle it. */
+       return 1;
     }
 }
 /* This is to be called when we catch a SIGSEGV/SIGBUS, determine that
@@ -4320,7 +4313,7 @@ void
 unhandled_sigmemoryfault()
 {}
 
-gc_alloc_update_all_page_tables(void)
+void gc_alloc_update_all_page_tables(void)
 {
     /* Flush the alloc regions updating the tables. */
     struct thread *th;
index 8d2ae21..98ad085 100644 (file)
@@ -416,11 +416,6 @@ void arch_install_interrupt_handlers(void)
     undoably_install_low_level_interrupt_handler(SIGBUS,sigbus_handler);
 }
 
-void get_spinlock(lispobj *word, int value) {
-    /* FIXME: dummy definition */
-    *word = value;
-}
-
 
 lispobj funcall0(lispobj function)
 {
index 3a2c96e..42b8a1c 100644 (file)
@@ -1,6 +1,19 @@
 #ifndef _HPPA_ARCH_H
 #define _HPPA_ARCH_H
 
+
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    *word=value;               /* FIXME for threads */
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
 #define ARCH_HAS_NPC_REGISTER
 
 #endif /* _HPPA_ARCH_H */
index 81c8e09..a2ad72a 100644 (file)
  * files for more information.
  */
 
+
+/* As far as I can tell, what's going on here is:
+ *
+ * In the case of most signals, when Lisp asks us to handle the
+ * signal, the outermost handler (the one actually passed to UNIX) is
+ * either interrupt_handle_now(..) or maybe_now_maybe_later(..).
+ * In that case, the Lisp-level handler is stored in interrupt_handlers[..]
+ * and interrupt_low_level_handlers[..] is cleared.
+ *
+ * However, some signals need special handling, e.g. 
+ *
+ * o the SIGSEGV (for e.g. Linux) or SIGBUS (for e.g. FreeBSD) used by the
+ *   garbage collector to detect violations of write protection,
+ *   because some cases of such signals (e.g. GC-related violations of
+ *   write protection) are handled at C level and never passed on to
+ *   Lisp. For such signals, we still store any Lisp-level handler
+ *   in interrupt_handlers[..], but for the outermost handle we use
+ *   the value from interrupt_low_level_handlers[..], instead of the
+ *   ordinary interrupt_handle_now(..) or interrupt_handle_later(..).
+ *
+ * o the SIGTRAP (Linux/Alpha) which Lisp code uses to handle breakpoints,
+ *   pseudo-atomic sections, and some classes of error (e.g. "function
+ *   not defined").  This never goes anywhere near the Lisp handlers at all.
+ *   See runtime/alpha-arch.c and code/signal.lisp 
+ * 
+ * - WHN 20000728, dan 20010128 */
+
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "genesis/fdefn.h"
 #include "genesis/simple-fun.h"
 
+void run_deferred_handler(struct interrupt_data *data, void *v_context) ;
+static void store_signal_data_for_later (struct interrupt_data *data, 
+                                        void *handler, int signal,
+                                        siginfo_t *info, 
+                                        os_context_t *context);
+boolean interrupt_maybe_gc_int(int signal, siginfo_t *info, void *v_context);
+
+extern lispobj all_threads_lock;
+extern int countdown_to_gc;
+
+/*
+ * This is a workaround for some slightly silly Linux/GNU Libc
+ * behaviour: glibc defines sigset_t to support 1024 signals, which is
+ * more than the kernel.  This is usually not a problem, but becomes
+ * one when we want to save a signal mask from a ucontext, and restore
+ * it later into another ucontext: the ucontext is allocated on the
+ * stack by the kernel, so copying a libc-sized sigset_t into it will
+ * overflow and cause other data on the stack to be corrupted */
+
+#define REAL_SIGSET_SIZE_BYTES ((NSIG/8))
+
 void sigaddset_blockable(sigset_t *s)
 {
     sigaddset(s, SIGHUP);
@@ -53,6 +102,11 @@ void sigaddset_blockable(sigset_t *s)
     sigaddset(s, SIGWINCH);
     sigaddset(s, SIGUSR1);
     sigaddset(s, SIGUSR2);
+#ifdef LISP_FEATURE_SB_THREAD
+    /* don't block STOP_FOR_GC, we need to be able to interrupt threads
+     * for GC purposes even when they are blocked on queues etc */
+    sigaddset(s, SIG_INTERRUPT_THREAD);
+#endif
 }
 
 /* When we catch an internal error, should we pass it back to Lisp to
@@ -64,34 +118,6 @@ boolean internal_errors_enabled = 0;
 
 struct interrupt_data * global_interrupt_data;
 
-/* As far as I can tell, what's going on here is:
- *
- * In the case of most signals, when Lisp asks us to handle the
- * signal, the outermost handler (the one actually passed to UNIX) is
- * either interrupt_handle_now(..) or maybe_now_maybe_later(..).
- * In that case, the Lisp-level handler is stored in interrupt_handlers[..]
- * and interrupt_low_level_handlers[..] is cleared.
- *
- * However, some signals need special handling, e.g. 
- *
- * o the SIGSEGV (for e.g. Linux) or SIGBUS (for e.g. FreeBSD) used by the
- *   garbage collector to detect violations of write protection,
- *   because some cases of such signals (e.g. GC-related violations of
- *   write protection) are handled at C level and never passed on to
- *   Lisp. For such signals, we still store any Lisp-level handler
- *   in interrupt_handlers[..], but for the outermost handle we use
- *   the value from interrupt_low_level_handlers[..], instead of the
- *   ordinary interrupt_handle_now(..) or interrupt_handle_later(..).
- *
- * o the SIGTRAP (Linux/Alpha) which Lisp code uses to handle breakpoints,
- *   pseudo-atomic sections, and some classes of error (e.g. "function
- *   not defined").  This never goes anywhere near the Lisp handlers at all.
- *   See runtime/alpha-arch.c and code/signal.lisp 
- * 
- * - WHN 20000728, dan 20010128 */
-
-
-boolean maybe_gc_pending = 0;
 \f
 /*
  * utility routines used by various signal handlers
@@ -189,6 +215,10 @@ fake_foreign_function_call(os_context_t *context)
     foreign_function_call_active = 1;
 }
 
+/* blocks all blockable signals.  If you are calling from a signal handler,
+ * the usual signal mask will be restored from the context when the handler 
+ * finishes.  Otherwise, be careful */
+
 void
 undo_fake_foreign_function_call(os_context_t *context)
 {
@@ -202,13 +232,7 @@ undo_fake_foreign_function_call(os_context_t *context)
     /* going back into Lisp */
     foreign_function_call_active = 0;
 
-    /* Undo dynamic binding. */
-    /* ### Do I really need to unbind_to_here()? */
-    /* FIXME: Is this to undo the binding of
-     * FREE_INTERRUPT_CONTEXT_INDEX? If so, we should say so. And
-     * perhaps yes, unbind_to_here() really would be clearer and less
-     * fragile.. */
-    /* dan (2001.08.10) thinks the above supposition is probably correct */
+    /* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */
     unbind(thread);
 
 #ifdef reg_ALLOC
@@ -258,81 +282,29 @@ interrupt_internal_error(int signal, siginfo_t *info, os_context_t *context,
     }
 }
 
-/* This function handles pending interrupts.  Note that in C/kernel
- * terms we dealt with the signal already; we just haven't decided
- * whether to call a Lisp handler or do a GC or something like that.
- * If it helps, you can think of pending_{signal,mask,info} as a
- * one-element queue of signals that we have acknowledged but not
- * processed */
-
 void
 interrupt_handle_pending(os_context_t *context)
 {
     struct thread *thread;
     struct interrupt_data *data;
 
-#ifndef __i386__
-    boolean were_in_lisp = !foreign_function_call_active;
-#endif
-#ifdef LISP_FEATURE_SB_THREAD
-    while(stop_the_world) kill(getpid(),SIGSTOP);
-#endif
     thread=arch_os_get_current_thread();
     data=thread->interrupt_data;
     SetSymbolValue(INTERRUPT_PENDING, NIL,thread);
 
-    if (maybe_gc_pending) {
-#ifndef __i386__
-       if (were_in_lisp)
-#endif
-       {
-           fake_foreign_function_call(context);
-       }
-       funcall0(SymbolFunction(SUB_GC));
-#ifndef __i386__
-       if (were_in_lisp)
-#endif
-       {
-           undo_fake_foreign_function_call(context);
-        }
-    }
+    /* restore the saved signal mask from the original signal (the
+     * one that interrupted us during the critical section) into the
+     * os_context for the signal we're currently in the handler for.
+     * This should ensure that when we return from the handler the
+     * blocked signals are unblocked */
+
+    memcpy(os_context_sigmask_addr(context), &data->pending_mask, 
+          REAL_SIGSET_SIZE_BYTES);
 
-    /* FIXME: This isn't very clear. It would be good to reverse
-     * engineer it and rewrite the code more clearly, or write a clear
-     * explanation of what's going on in the comments, or both.
-     *
-     * WHN's question 1a: How come we unconditionally copy from
-     * pending_mask into the context, and then test whether
-     * pending_signal is set?
-     * 
-     * WHN's question 1b: If pending_signal wasn't set, how could
-     * pending_mask be valid?
-     * 
-     * Dan Barlow's reply (sbcl-devel 2001-03-13): And the answer is -
-     * or appears to be - because interrupt_maybe_gc set it that way
-     * (look in the #ifndef __i386__ bit). We can't GC during a
-     * pseudo-atomic, so we set maybe_gc_pending=1 and
-     * arch_set_pseudo_atomic_interrupted(..) When we come out of
-     * pseudo_atomic we're marked as interrupted, so we call
-     * interrupt_handle_pending, which does the GC using the pending
-     * context (it needs a context so that it has registers to use as
-     * GC roots) then notices there's no actual interrupt handler to
-     * call, so doesn't. That's the second question [1b] answered,
-     * anyway. Why we still need to copy the pending_mask into the
-     * context given that we're now done with the context anyway, I
-     * couldn't say. */
-#if 0
-    memcpy(os_context_sigmask_addr(context), &pending_mask, 
-          4 /* sizeof(sigset_t) */ );
-#endif
     sigemptyset(&data->pending_mask);
-    if (data->pending_signal) {
-       int signal = data->pending_signal;
-       siginfo_t info;
-       memcpy(&info, &data->pending_info, sizeof(siginfo_t));
-       data->pending_signal = 0;
-       interrupt_handle_now(signal, &info, context);
-    }
+    /* This will break on sparc linux: the deferred handler really wants
+     * to be called with a void_context */
+    run_deferred_handler(data,(void *)context);        
 }
 \f
 /*
@@ -394,11 +366,18 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
        lose("no handler for signal %d in interrupt_handle_now(..)", signal);
 
     } else if (lowtag_of(handler.lisp) == FUN_POINTER_LOWTAG) {
+       /* Once we've decided what to do about contexts in a 
+        * return-elsewhere world (the original context will no longer
+        * be available; should we copy it or was nobody using it anyway?)
+        * then we should convert this to return-elsewhere */
+
+        /* CMUCL comment said "Allocate the SAPs while the interrupts
+        * are still disabled.".  I (dan, 2003.08.21) assume this is 
+        * because we're not in pseudoatomic and allocation shouldn't
+        * be interrupted.  In which case it's no longer an issue as
+        * all our allocation from C now goes through a PA wrapper,
+        * but still, doesn't hurt */
 
-        /* Allocate the SAPs while the interrupts are still disabled.
-        * (FIXME: Why? This is the way it was done in CMU CL, and it
-        * even had the comment noting that this is the way it was
-        * done, but no motivation..) */
         lispobj info_sap,context_sap = alloc_sap(context);
         info_sap = alloc_sap(info);
         /* Allow signals again. */
@@ -438,16 +417,70 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
 #endif
 }
 
+/* This is called at the end of a critical section if the indications
+ * are that some signal was deferred during the section.  Note that as
+ * far as C or the kernel is concerned we dealt with the signal
+ * already; we're just doing the Lisp-level processing now that we
+ * put off then */
+
+void
+run_deferred_handler(struct interrupt_data *data, void *v_context) {
+    (*(data->pending_handler))
+       (data->pending_signal,&(data->pending_info), v_context);
+}
+
+boolean
+maybe_defer_handler(void *handler, struct interrupt_data *data,
+                   int signal, siginfo_t *info, os_context_t *context)
+{
+    struct thread *thread=arch_os_get_current_thread();
+    if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) {
+       store_signal_data_for_later(data,handler,signal,info,context);
+        SetSymbolValue(INTERRUPT_PENDING, T,thread);
+       return 1;
+    } 
+    /* a slightly confusing test.  arch_pseudo_atomic_atomic() doesn't
+     * actually use its argument for anything on x86, so this branch
+     * may succeed even when context is null (gencgc alloc()) */
+    if (
+#ifndef __i386__
+       (!foreign_function_call_active) &&
+#endif
+       arch_pseudo_atomic_atomic(context)) {
+       store_signal_data_for_later(data,handler,signal,info,context);
+       arch_set_pseudo_atomic_interrupted(context);
+       return 1;
+    }
+    return 0;
+}
 static void
-store_signal_data_for_later (struct interrupt_data *data, int signal, 
+store_signal_data_for_later (struct interrupt_data *data, void *handler,
+                            int signal, 
                             siginfo_t *info, os_context_t *context)
 {
+    data->pending_handler = handler;
     data->pending_signal = signal;
-    memcpy(&(data->pending_info), info, sizeof(siginfo_t));
-    memcpy(&(data->pending_mask),
-          os_context_sigmask_addr(context),
-          sizeof(sigset_t));
-    sigaddset_blockable(os_context_sigmask_addr(context));
+    if(info)
+       memcpy(&(data->pending_info), info, sizeof(siginfo_t));
+    if(context) {
+       /* the signal mask in the context (from before we were
+        * interrupted) is copied to be restored when
+        * run_deferred_handler happens.  Then the usually-blocked
+        * signals are added to the mask in the context so that we are
+        * running with blocked signals when the handler returns */
+       sigemptyset(&(data->pending_mask));
+       memcpy(&(data->pending_mask),
+              os_context_sigmask_addr(context),
+              REAL_SIGSET_SIZE_BYTES);
+       sigaddset_blockable(os_context_sigmask_addr(context));
+    } else {
+       /* this is also called from gencgc alloc(), in which case
+        * there has been no signal and is therefore no context. */
+       sigset_t new;
+       sigemptyset(&new);
+       sigaddset_blockable(&new);
+       sigprocmask(SIG_BLOCK,&new,&(data->pending_mask));
+    }
 }
 
 
@@ -460,24 +493,35 @@ maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 #ifdef LISP_FEATURE_LINUX
     os_restore_fp_control(context);
 #endif 
-    /* see comments at top of code/signal.lisp for what's going on here
-     * with INTERRUPTS_ENABLED/INTERRUPT_HANDLE_NOW 
-     */
-    if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) {
-       store_signal_data_for_later(data,signal,info,context);
-        SetSymbolValue(INTERRUPT_PENDING, T,thread);
-    } else if (
-#ifndef __i386__
-              (!foreign_function_call_active) &&
-#endif
-              arch_pseudo_atomic_atomic(context)) {
-       store_signal_data_for_later(data,signal,info,context);
-       arch_set_pseudo_atomic_interrupted(context);
-    } else {
-        interrupt_handle_now(signal, info, context);
+    if(maybe_defer_handler(interrupt_handle_now,data,
+                          signal,info,context))
+       return;
+    interrupt_handle_now(signal, info, context);
+}
+
+void
+sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
+{
+    os_context_t *context = arch_os_get_context(&void_context);
+    struct thread *thread=arch_os_get_current_thread();
+    struct interrupt_data *data=thread->interrupt_data;
+    sigset_t block;
+
+    if(maybe_defer_handler(sig_stop_for_gc_handler,data,
+                          signal,info,context)){
+       return;
     }
+    sigemptyset(&block);
+    sigaddset_blockable(&block);
+    sigprocmask(SIG_BLOCK, &block, 0);
+    get_spinlock(&all_threads_lock,thread->pid);
+    countdown_to_gc--;
+    release_spinlock(&all_threads_lock);
+    /* need the context stored so it can have registers scavenged */
+    fake_foreign_function_call(context); 
+    kill(getpid(),SIGSTOP);
+    undo_fake_foreign_function_call(context);
 }
-\f
 
 void
 interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
@@ -576,16 +620,20 @@ void arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 }
 
 #ifdef LISP_FEATURE_SB_THREAD
-boolean handle_rt_signal(int num, siginfo_t *info, void *v_context)
+void handle_rt_signal(int num, siginfo_t *info, void *v_context)
 {
-    struct 
-       os_context_t *context = (os_context_t*)arch_os_get_context(&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;
+    if(maybe_defer_handler(handle_rt_signal,data,num,info,context)){
+       return ;
+    }
     arrange_return_to_lisp_function(context,info->si_value.sival_int);
 }
 #endif
 
-boolean handle_control_stack_guard_triggered(os_context_t *context,void *addr)
-{
+boolean handle_control_stack_guard_triggered(os_context_t *context,void *addr){
     struct thread *th=arch_os_get_current_thread();
     /* note the os_context hackery here.  When the signal handler returns, 
      * it won't go back to what it was doing ... */
@@ -619,67 +667,36 @@ interrupt_maybe_gc(int signal, siginfo_t *info, void *void_context)
 
     if(!foreign_function_call_active && gc_trigger_hit(signal, info, context)){
        clear_auto_gc_trigger();
-
-       if (arch_pseudo_atomic_atomic(context)) {
-           /* don't GC during an atomic operation.  Instead, copy the 
-            * signal mask somewhere safe.  interrupt_handle_pending
-            * will detect pending_signal==0 and know to do a GC with the
-            * signal context instead of calling a Lisp-level handler */
-           maybe_gc_pending = 1;
-           if (data->pending_signal == 0) {
-               /* FIXME: This copy-pending_mask-then-sigaddset_blockable
-                * idiom occurs over and over. It should be factored out
-                * into a function with a descriptive name. */
-               memcpy(&(data->pending_mask),
-                      os_context_sigmask_addr(context),
-                      sizeof(sigset_t));
-               sigaddset_blockable(os_context_sigmask_addr(context));
-           }
-           arch_set_pseudo_atomic_interrupted(context);
-       }
-       else {
-           fake_foreign_function_call(context);
-           /* SUB-GC may return without GCing if *GC-INHIBIT* is set,
-            * in which case we will be running with no gc trigger
-            * barrier thing for a while.  But it shouldn't be long 
-            * until the end of WITHOUT-GCING. */
-           funcall0(SymbolFunction(SUB_GC));
-           undo_fake_foreign_function_call(context);
-       }       
+       if(!maybe_defer_handler
+          (interrupt_maybe_gc_int,data,signal,info,void_context))
+           interrupt_maybe_gc_int(signal,info,void_context);
        return 1;
-    } else {
-       return 0;
     }
+    return 0;
 }
+
 #endif
+
+/* this is also used by from gencgc.c alloc() */
+boolean
+interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
+{
+    os_context_t *context=(os_context_t *) void_context;
+    fake_foreign_function_call(context);
+    /* SUB-GC may return without GCing if *GC-INHIBIT* is set, in
+     * which case we will be running with no gc trigger barrier
+     * thing for a while.  But it shouldn't be long until the end
+     * of WITHOUT-GCING. */
+    funcall0(SymbolFunction(SUB_GC));
+    undo_fake_foreign_function_call(context);
+    return 1;
+}
+
 \f
 /*
  * noise to install handlers
  */
 
-/* SBCL used to have code to restore signal handlers on exit, which
- * has been removed from the threaded version until we decide: exit of
- * _what_ ? */
-
-/* SBCL comment: The "undoably" aspect is because we also arrange with
- * atexit() for the handler to be restored to its old value. This is
- * for tidiness: it shouldn't matter much ordinarily, but it does
- * remove a window where e.g. memory fault signals (SIGSEGV or SIGBUS,
- * which in ordinary operation of SBCL are sent to the generational
- * garbage collector, then possibly onward to Lisp code) or SIGINT
- * (which is ordinarily passed to Lisp code) could otherwise be
- * handled bizarrely/brokenly because the Lisp code would try to deal
- * with them using machinery (like stream output buffers) which has
- * already been dismantled. */
-
-/* I'm not sure (a) whether this is a real concern, (b) how it helps
-   anyway */
-
-void
-uninstall_low_level_interrupt_handlers_atexit(void)
-{
-}
-
 void
 undoably_install_low_level_interrupt_handler (int signal,
                                              void handler(int,
index f725900..35e221d 100644 (file)
@@ -32,8 +32,9 @@ struct interrupt_data {
     void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*) ;
     union interrupt_handler interrupt_handlers[NSIG];
 
-    /* signal number, siginfo_t, and old mask information for pending
-     * signal.  pending_signal=0 when there is no pending signal. */
+    /* signal information for pending signal.  pending_signal=0 when there 
+     * is no pending signal. */
+    void (*pending_handler) (int, siginfo_t*, void*) ;
     int pending_signal ;
     siginfo_t pending_info;
     sigset_t pending_mask;
@@ -50,7 +51,8 @@ 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_rt_signal(int, siginfo_t*, void*);
+extern void handle_rt_signal(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,
                                                          void
@@ -71,4 +73,6 @@ void sigaddset_blockable(sigset_t *s);
  * signal(..) handlers have another, and attempting to represent them
  * "cleanly" with union types is in fact a mess. */
 #define ARE_SAME_HANDLER(x, y) ((void*)(x) == (void*)(y))
+
 #endif
+
index b57b510..ea2f1b0 100644 (file)
@@ -28,6 +28,7 @@
 #include "os.h"
 #include "arch.h"
 #include "globals.h"
+#include "sbcl.h"
 #include "interrupt.h"
 #include "interr.h"
 #include "lispregs.h"
@@ -248,10 +249,12 @@ sigsegv_handler(int signal, siginfo_t *info, void* void_context)
 
 void sigcont_handler(int signal, siginfo_t *info, void *void_context)
 {
-    /* we need to have a handler installed for this signal so that
-     * sigwaitinfo() for it actually returns at the appropriate time
-     */
-    fprintf(stderr, "Thread %d received stray SIGCONT\n", getpid());
+    /* We need to have a handler installed for this signal so that
+     * sigwaitinfo() for it actually returns at the appropriate time.
+     * We don't need it to actually do anything.  This mkes it
+     * possibly the only signal handler in SBCL that doesn't depend on
+     * not-guaranteed-by-POSIX features 
+     */    
 }
 
 void
@@ -262,6 +265,8 @@ os_install_interrupt_handlers(void)
 #ifdef LISP_FEATURE_SB_THREAD
     undoably_install_low_level_interrupt_handler(SIG_INTERRUPT_THREAD,
                                                 handle_rt_signal);
+    undoably_install_low_level_interrupt_handler(SIG_STOP_FOR_GC,
+                                                sig_stop_for_gc_handler);
 #endif
     undoably_install_low_level_interrupt_handler(SIGCONT,
                                                 sigcont_handler);
index 4303f34..cec30a5 100644 (file)
@@ -38,4 +38,6 @@ typedef int os_vm_prot_t;
 
 #define SIG_MEMORY_FAULT SIGSEGV
 #define SIG_INTERRUPT_THREAD SIGRTMIN
+#define SIG_STOP_FOR_GC (SIGRTMIN+1)
+
 
index c1ca525..7b68e65 100644 (file)
@@ -351,11 +351,6 @@ void arch_install_interrupt_handlers()
     undoably_install_low_level_interrupt_handler(SIGFPE,sigfpe_handler);
 }
 
-void get_spinlock(lispobj *word, int value) {
-    /* FIXME: dummy definition */
-    *word = value;
-}
-
 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
 
 lispobj funcall0(lispobj function)
index 17c1886..50b0776 100644 (file)
@@ -1,4 +1,17 @@
 #ifndef _MIPS_ARCH_H
 #define _MIPS_ARCH_H
 
+
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    *word=value;               /* FIXME for threads */
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
 #endif /* _MIPS_ARCH_H */
index c35fc4c..9c20cc0 100644 (file)
@@ -95,14 +95,6 @@ arch_install_breakpoint(void *pc)
 }
 
 void 
-get_spinlock(lispobj *word,int value)
-{
-    /* FIXME */
-    *word=value;
-}
-
-
-void 
 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
 {
     *(unsigned long *)pc = orig_inst;
index b43e78e..cbaa670 100644 (file)
@@ -1,6 +1,19 @@
 #ifndef _PPC_ARCH_H
 #define _PPC_ARCH_H
 
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    *word=value;               /* FIXME for threads */
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
+
 #define ARCH_HAS_LINK_REGISTER
 
 #endif /* _PPC_ARCH_H */
index b154611..32cad8a 100644 (file)
@@ -379,67 +379,6 @@ static void parent_sighandler(int signum,siginfo_t *info, void *void_context)
 }
 
 #ifdef LISP_FEATURE_SB_THREAD
-static void parent_do_garbage_collect(void)
-{    
-    int waiting_threads=0;
-    struct thread *th;
-    int status,p;
-
-    for_each_thread(th) {
-       if(ptrace(PTRACE_ATTACH,th->pid,0,0)) {
-           perror("PTRACE_ATTACH");
-       }
-       else waiting_threads++;
-    }
-    stop_the_world=1;
-
-    do {
-       /* not sure if we have to wait for PTRACE_ATTACH to finish
-        * before we can send PTRACE_CONT, so let's play it safe
-        */
-       while(waiting_threads>0) {
-           if((p=waitpid(-1,&status, WUNTRACED|__WALL))>0) {
-               if(WIFEXITED(status) || WIFSIGNALED(status)) 
-                   destroy_thread(find_thread_by_pid(p));
-               else {
-#if 0
-                   fprintf(stderr, "wait returned pid %d signal %x\n",
-                           p,WSTOPSIG(status));
-#endif
-                   if(WSTOPSIG(status)==SIGTRAP) {
-                       if(ptrace(PTRACE_CONT,p,0,SIGTRAP))
-                           perror("PTRACE_CONT");
-                   }
-                   else waiting_threads--; 
-               }
-           }
-       }
-       for_each_thread(th) {
-           if(SymbolTlValue(PSEUDO_ATOMIC_ATOMIC,th)) {
-               /* restart the child, setting *p-a-i* which will cause it 
-                * to go into interrupt_handle_pending as soon as it's
-                * finished being pseudo_atomic.  once there it will
-                * signal itself SIGSTOP, which will give us another 
-                * event to wait for */
-#if 0
-               fprintf(stderr, "%d was pseudo-atomic, letting it resume \n",
-                       th->pid);
-#endif
-               SetTlSymbolValue(PSEUDO_ATOMIC_INTERRUPTED,make_fixnum(1),th);
-               if(ptrace(PTRACE_CONT,th->pid,0,0))
-                   perror("PTRACE_CONT");
-               waiting_threads++;
-           }
-       }
-    } while (waiting_threads>0);
-               
-    collect_garbage(maybe_gc_pending-1);
-    maybe_gc_pending=0;
-    stop_the_world=0;
-    for_each_thread(th) 
-       if(ptrace(PTRACE_DETACH,th->pid,0,0))
-           perror("PTRACE_DETACH");
-}
 
 static void /* noreturn */ parent_loop(void)
 {
@@ -468,11 +407,9 @@ static void /* noreturn */ parent_loop(void)
     while(!all_threads) {
        sched_yield();
     }
-    maybe_gc_pending=0;
     while(all_threads && (pid=waitpid(-1,&status,__WALL|WUNTRACED))) {
        struct thread *th;
        int real_errno=errno;
-       while(maybe_gc_pending) parent_do_garbage_collect();
        if(pid==-1) {
            if(real_errno == EINTR) {
                continue;
@@ -485,9 +422,7 @@ static void /* noreturn */ parent_loop(void)
        if(!th) continue;
        if(WIFEXITED(status) || WIFSIGNALED(status)) {
            fprintf(stderr,"waitpid : child %d %x exited \n", pid,th);
-           destroy_thread(th);         
-           /* FIXME arrange to call or fake (free-mutex *session-lock*)
-            * if necessary */
+           destroy_thread(th);
            if(!all_threads) break;
        }
     }
index e68eca5..bd1e663 100644 (file)
@@ -351,11 +351,6 @@ void arch_install_interrupt_handlers()
     undoably_install_low_level_interrupt_handler(SIGEMT, sigemt_handler);
 }
 
-void get_spinlock(lispobj *word, int value) {
-    /* FIXME: dummy definition */
-    *word = value;
-}
-
 \f
 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
 
index b75247d..52c2578 100644 (file)
@@ -1,6 +1,18 @@
 #ifndef _SPARC_ARCH_H
 #define _SPARC_ARCH_H
 
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    *word=value;               /* FIXME for threads */
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
 #define ARCH_HAS_NPC_REGISTER
 
 #endif /* _SPARC_ARCH_H */
index ffd14db..78a3ec3 100644 (file)
@@ -24,6 +24,7 @@
 int dynamic_values_bytes=4096*sizeof(lispobj); /* same for all threads */
 struct thread *all_threads;
 lispobj all_threads_lock;
+int countdown_to_gc;
 extern struct interrupt_data * global_interrupt_data;
 
 void get_spinlock(lispobj *word,int value);
@@ -211,7 +212,7 @@ pid_t create_thread(lispobj initial_function) {
      * to ensure that we don't have >1 thread with pid=0 on the list at once
      */
     protect_control_stack_guard_page(th->pid,1);
-    all_threads_lock=0;
+    release_spinlock(&all_threads_lock);
     th->pid=kid_pid;           /* child will not start until this is set */
 #ifndef LISP_FEATURE_SB_THREAD
     new_thread_trampoline(all_threads);        /*  call_into_lisp */
@@ -235,6 +236,7 @@ void destroy_thread (struct thread *th)
     gc_alloc_update_page_tables(0, &th->alloc_region);
 #endif
     get_spinlock(&all_threads_lock,th->pid);
+    if(countdown_to_gc>0) countdown_to_gc--;
     if(th==all_threads) 
        all_threads=th->next;
     else {
@@ -242,7 +244,7 @@ void destroy_thread (struct thread *th)
        while(th1->next!=th) th1=th1->next;
        th1->next=th->next;     /* unlink */
     }
-    all_threads_lock=0;
+    release_spinlock(&all_threads_lock);
     if(th && th->tls_cookie>=0) arch_os_thread_cleanup(th); 
     os_invalidate((os_vm_address_t) th->control_stack_start,
                  ((sizeof (lispobj))
@@ -289,6 +291,44 @@ int interrupt_thread(pid_t pid, lispobj function)
     union sigval sigval;
     sigval.sival_int=function;
 
-    sigqueue(pid, SIG_INTERRUPT_THREAD, sigval);
+    return sigqueue(pid, SIG_INTERRUPT_THREAD, sigval);
+}
+
+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();
+    struct thread *tail=0;
+    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;
+               countdown_to_gc++;
+               kill(p->pid,SIG_STOP_FOR_GC);
+           }
+           tail=all_threads;
+       } else {
+           finished=(countdown_to_gc==0);
+       }
+       release_spinlock(&all_threads_lock);
+       sched_yield();
+    } while(!finished);
+}
+
+void gc_start_the_world()
+{
+    struct thread *p,*th=arch_os_get_current_thread();
+    get_spinlock(&all_threads_lock,th->pid);
+    for(p=all_threads;p;p=p->next) {
+       if(p==th) continue;
+       kill(p->pid,SIGCONT);
+    }
+    release_spinlock(&all_threads_lock);
 }
 #endif
index f1a1df9..b99b48a 100644 (file)
@@ -88,6 +88,13 @@ static inline void SetTlSymbolValue(u32 tagged_symbol_pointer,lispobj val, void
 #endif
 }
 
+static inline os_context_t *get_interrupt_context_for_thread(struct thread *th)
+{
+    return th->interrupt_contexts
+       [fixnum_value(SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX,th)-1)];
+}
+
+
 int arch_os_thread_init(struct thread *thread);
 extern struct thread *arch_os_get_current_thread();
 
index 24adeca..9b51cc0 100644 (file)
@@ -141,19 +141,6 @@ arch_install_breakpoint(void *pc)
     return result;
 }
 
-void 
-get_spinlock(lispobj *word,int value)
-{
-    u32 eax=0;
-    do {
-       asm ("xor %0,%0\n\
-              lock cmpxchg %1,%2" 
-            : "=a" (eax)
-            : "r" (value), "m" (*word)
-            : "memory", "cc");
-    } while(eax!=0);
-}
-
 void
 arch_remove_breakpoint(void *pc, unsigned long orig_inst)
 {
index bbcdcc9..19d6cf6 100644 (file)
  * here? (The answer wasn't obvious to me when merging the
  * architecture-abstracting patches for CSR's SPARC port. -- WHN 2002-02-15) */
 
+static inline void 
+get_spinlock(lispobj *word,int value)
+{
+    u32 eax=0;
+    do {
+       asm ("xor %0,%0\n\
+              lock cmpxchg %1,%2" 
+            : "=a" (eax)
+            : "r" (value), "m" (*word)
+            : "memory", "cc");
+    } while(eax!=0);
+}
+
+static inline void
+release_spinlock(lispobj *word)
+{
+    *word=0;
+}
+
 #endif /* _X86_ARCH_H */
index 07b8e33..20d834e 100644 (file)
@@ -51,9 +51,8 @@ size_t os_vm_page_size;
 
 u32 local_ldt_copy[LDT_ENTRIES*LDT_ENTRY_SIZE/sizeof(u32)];
 
-/* XXX this could be conditionally compiled based on some
- * "debug-friendly" flag.  But it doesn't really make stuff slower,
- * just the runtime gets fractionally larger */
+/* This is never actually called, but it's great for calling from gdb when
+ * users have thread-related problems that maintainers can't duplicate */
 
 void debug_get_ldt()
 { 
index 0477691..18a3376 100644 (file)
     child))
 
 ;;; separate tests for (a) interrupting Lisp code, (b) C code, (c) a syscall,
-;;; (d) waiting on a lock
+;;; (d) waiting on a lock, (e) some code which we hope is likely to be
+;;; in pseudo-atomic
 
 (let ((child (test-interrupt (lambda () (loop)))))  (terminate-thread child))
 
                     (assert (eql (mutex-value lock) (current-thread-id))))
                   (assert (not (eql (mutex-value lock) (current-thread-id)))))))
     ;;hold onto lock for long enough that child can't get it immediately
-    (sleep 5))
+    (sleep 20)
+    (interrupt-thread child (lambda () (format t "l ~A~%" (mutex-value lock))))
+    (format t "parent releasing lock~%"))
   (terminate-thread child))
 
-;; better would be "wait until all child threads have exited"
-(sleep 3)
+(defun alloc-stuff () (copy-list '(1 2 3 4 5)))
+(let ((c (test-interrupt (lambda () (loop (alloc-stuff))))))
+  ;; NB this only works on x86
+  (dotimes (i 70)
+    (sleep (random 1d0))
+    (interrupt-thread c
+                     (lambda ()
+                       (princ ".") (force-output)
+                       (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)))
+  (loop
+   (when (zerop done) (return))
+   (sleep 1)))
+
+;; give the other thread time to die before we leave, otherwise the
+;; overall exit status is 0, not 104
+(sleep 2) 
 
 (sb-ext:quit :unix-status 104)
index ec0930a..94b9c85 100644 (file)
@@ -16,5 +16,4 @@
 ;;; with something arbitrary in the fourth field, is used for CVS
 ;;; 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.3"
+"0.8.3.1"