From: Matt Birkholz Date: Sat, 21 Feb 2015 18:33:00 +0000 (-0700) Subject: smp: README.txt wordsmithing. Squash into 9969ff0. X-Git-Url: https://birchwood-abbey.net/git?a=commitdiff_plain;h=fe0221e4e8299f3e34571161a23e418e85d51a22;p=mit-scheme.git smp: README.txt wordsmithing. Squash into 9969ff0. --- diff --git a/README.txt b/README.txt index cb39dab51..066b85137 100644 --- a/README.txt +++ b/README.txt @@ -63,34 +63,38 @@ and continue). 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 @@ -101,8 +105,8 @@ until it succeeds and can re-arrange the heap. 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 @@ -112,23 +116,22 @@ All of the operations that manipulate Scheme threads, including 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. * Analyses