The first primitive executed by the main pthread is typically (outside
of the initial bootstrap) Prim_band_load, which begins with
-smp_gc_start. Without a fixed objects vector, new processors cannot
-handle the usual global-gc signal, so they start out in the GC-WAIT
-state via the SMP-PAUSE primitive.
-
-To avoid signaling the new processors, the main pthread waits for them
-all to reach the GC-WAIT state and signal "ready". When ready, the
-main pthread runs the interpreter to bootstrap or load a band. The
-waiting processors are blocked until the finished condition is
-signaled by the primitive GC daemon. By that point, a bootstrap or
+smp_gc_start. At this point there is no fixed objects vector, so the
+GC cannot start by signaling any new processors. Instead the new
+processors START in the gc-wait state via Scheme's smp-pause
+primitive.
+
+The main pthread avoids signaling new processors by waiting (on the
+"ready" signal) for all processors to reach the gc-wait state. When
+ready, the main pthread runs the interpreter to bootstrap or load a
+band. The waiting processors are blocked until the finished condition
+is signaled by the primitive GC daemon. By that point, a bootstrap or
band restore will have initialized the interrupt handlers in the fixed
-objects vector. The non-main processors continue from smp-pause by
-"restarting" smp-idle where they wait for a timer interrupt
-(e.g. when the main thread creates the first non-main thread).
+objects vector.
+
+A processor continues from smp-pause by "restarting" a different
+primitive -- smp-idle. In smp-idle, they wait for a timer interrupt.
+The first such interrupt comes when the main thread creates the first
+non-main thread.
** Synchronizing for GC
A processor aborting for GC locks the state_mutex and test-and-sets
the gc_processor variable. If successful, it proceeds to interrupt
the other processors, whose interrupt handlers cause them to execute
-smp-gc-wait, a primitive that shifts them into GC-WAIT state where
+smp-gc-wait, a primitive that shifts them into gc-wait state where
they block on the finished condition. gc_processor waits for the
-others to reach the GC-WAIT state by blocking on the ready condition.
+others to reach the gc-wait state by blocking on the ready condition.
When ready is signaled, the GC processor performs the garbage
collection and broadcasts the finished condition.
A processor aborting for GC may lock the state_mutex only to find
another processor has already set the gc_processor variable. If it
-does, it goes straight to the GC-WAIT state and blocks on the finished
+does, it goes straight to the gc-wait state and blocks on the finished
condition, then proceeds with an empty local heap as though it had
accomplished the garbage collection itself. If the primitive being
executed needs exclusive access to reformat the heap (a purification
The runtime system keeps track of the threads running on each
processor by putting them in a "current-threads" vector. Newly
-runnable threads are put on a runnable queue (and an idle processor is
-woken, if any). The timer interrupt handler should pick up a runnable
+runnable threads are put on a runnable queue and an idle processor is
+woken, if any. The timer interrupt handler should pick up a runnable
thread and process its continuation. When a thread is no longer
runnable and there are no runnable threads in the queue, the processor
executes either test-select-registry (when it is the io-waiter) or
current-threads, the runnable queue, timers and i/o channel waits,
must be serialized. They must grab the thread_mutex and hold it for
the duration of the operation. This mutex is distinct from the
-state_mutex so that it can serialize the thread operations, without
+state_mutex so that it can serialize the thread operations without
locking out GCs.
An important Scheme thread operation is the timer interrupt handler.
It must grab the thread_mutex because other threads may be spawning or
signaling threads and thus frobbing the runnable queue and whatnot
too. An interrupt handler that grabs a mutex, interrupting code that
-may hold that mutex, is courting deadlock, so care must be taken to
+may hold that mutex, is courting deadlock, so care is taken to
mask the interrupt as long as (longer than!) the mutex is held.
-The OS process receives a timer interrupt (SIGALRM) regularly while
-there are threads in the runnable queue, and whenever a thread would
-wake from sleep. The signal handler might run in any pthread, but
-is forwarded to the next processor in a ring of all processors.
-Thus 2 processors are interrupted half as often with the same thread
-timer interval. Four processors run uninterrupted for 4 times the
-interval.
+The OS process receives a timer signal (SIGALRM) regularly while there
+are threads in the runnable queue (or when a thread wakes). The
+signal handler might run in any pthread, but is forwarded to the next
+processor in the ring of all processors. Thus 2 processors are
+interrupted half as often with the same thread timer interval. Four
+processors run uninterrupted for 4 times the interval.
\f
* Analyses