/* The chain of processors, starting with processor0 -- main()'s thread: */
processor_t *processors;
+/* The mutex that serializes processor state changes, including the
+ gc_processor variable. */
+pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The processor running the garbage collector. */
+processor_t *gc_processor = NULL;
+
+/* The condition variable on which GC-WAIT processors wait. */
+static pthread_cond_t finished = PTHREAD_COND_INITIALIZER;
+
+/* The condition variable on which the gc_processor waits. */
+static pthread_cond_t ready = PTHREAD_COND_INITIALIZER;
+
/* The mutex that serializes thread switches. */
static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
+static void
+fatal (const char * format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ voutf_fatal (format, args);
+ va_end (args);
+ outf_flush_fatal ();
+ self->free_pointer = Free;
+ self->stack_pointer = stack_pointer;
+ self->history_register = history_register;
+ self->state = PROCESSOR_DEAD;
+ pthread_exit ((void*)self);
+ /* NOTREACHED */
+}
+
+static void
+mutex_lock (pthread_mutex_t *mutex)
+{
+ int err = pthread_mutex_lock (mutex);
+ if (err)
+ fatal ("\n;%d pthread_mutex_lock failed: %d", self->id, err);
+}
+
+static void
+mutex_unlock (pthread_mutex_t *mutex)
+{
+ int err = pthread_mutex_unlock (mutex);
+ if (err)
+ fatal ("\n;%d pthread_mutex_unlock failed: %d", self->id, err);
+}
+
+static void
+cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ int err = pthread_cond_wait (cond, mutex);
+ if (err)
+ fatal ("\n;%d pthread_cond_wait failed: %d", self->id, err);
+}
+
+static void
+cond_broadcast (pthread_cond_t *cond)
+{
+ int err = pthread_cond_broadcast (cond);
+ if (err)
+ fatal ("\n;%d pthread_cond_broadcast failed: %d", self->id, err);
+}
+
+static void
+create (int id, pthread_t *thread, void *(*start_routine) (void *), void *arg)
+{
+ int err = pthread_create (thread, NULL, start_routine, arg);
+ if (err)
+ fatal ("\n;%d pthread_create failed: %d", id, err);
+}
#endif
DEFINE_PRIMITIVE ("SMP-ID", Prim_smp_id, 0, 0, "(SMP-ID)\n\
new = malloc (sizeof (processor_t));
if (new == NULL)
{
- outf_fatal ("\n;%d could not malloc processor_t\n", id);
+ outf_fatal ("\n;%d could not malloc processor_t", id);
outf_flush_fatal ();
Microcode_Termination (TERM_NO_SPACE);
}
processors = new;
if (id != 0)
- {
- int err = pthread_create (&new->pthread, NULL, &work, new);
- if (err)
- {
- outf_fatal ("pthread_create failed: %d\n", err);
- outf_flush_fatal ();
- exit (1);
- }
- }
+ create (id, &new->pthread, &work, new);
trace (";%d heap: 0x%0lx-0x%0lx", id,
(ulong)new->heap_start, (ulong)new->heap_end);
if (id > 0)
make_processors (id - 1);
}
+
+static bool
+all_in (processor_state_t s)
+{
+ processor_t *p;
+ bool all = true;
+ for (p = processors; p != NULL; p = p->next)
+ if (p->state != s)
+ {
+ all = false;
+ break;
+ }
+ return (all);
+}
+\f
+static void
+export_state (void)
+{
+ self->free_pointer = Free;
+ self->stack_pointer = stack_pointer;
+ self->history_register = history_register;
+}
+
+static void
+import_state (void)
+{
+ Free = self->free_pointer;
+ history_register = self->history_register;
+ heap_alloc_limit = heap_end - heap_reserved;
+}
+
+static void
+gc_wait (void)
+{
+ trace (";%d Waiting.", self->id);
+ self->state = PROCESSOR_GC_WAIT;
+ export_state ();
+ if (all_in (PROCESSOR_GC_WAIT))
+ {
+ trace (";%d ALL waiting.", self->id);
+ cond_broadcast (&ready);
+ }
+ cond_wait (&finished, &state_mutex);
+
+ assert (self->state == PROCESSOR_RUNNING);
+ trace (";%d Done waiting.", self->id);
+ import_state ();
+}
+
+static void
+interrupt_others (void)
+{
+ processor_t *p;
+
+ for (p = processors; p != NULL; p = p->next)
+ if (p != self && p->state != PROCESSOR_GC_WAIT)
+ {
+ trace (";%d sending SIGUSR2 to pthread %#x", self->id, p->pthread);
+ smp_kill_gc (p->pthread);
+ }
+}
+
+void
+smp_gc_start (void)
+{
+ /* Wait to become gc_processor. If a GC has already started, wait
+ until it is finished, then try again (forever). Called by
+ primitives that need to be the gc_processor. gc_processor ==
+ self (if :-) upon return. */
+
+ trace (";%d GC start.", self->id);
+ mutex_lock (&state_mutex);
+ while (gc_processor != NULL)
+ {
+#ifdef ENABLE_DEBUGGING_TOOLS
+ int GCer = gc_processor->id;
+#endif
+ trace (";%d GC started by %d.", self->id, GCer);
+ gc_wait ();
+ trace (";%d GC finished by %d.", self->id, GCer);
+ }
+ gc_processor = self;
+ trace (";%d GC initiated.", self->id);
+ self->state = PROCESSOR_GC_WAIT;
+ interrupt_others ();
+ export_state ();
+ if (! all_in (PROCESSOR_GC_WAIT))
+ {
+ trace (";%d GC waiting.", self->id);
+ cond_wait (&ready, &state_mutex);
+ }
+ trace (";%d GC ready (all waiting).", self->id);
+ mutex_unlock (&state_mutex);
+}
+
+bool
+smp_gc_started (void)
+{
+ /* Wait to become gc_processor. If a GC has already started, wait
+ until it is finished, then return false. Else this thread is now
+ the gc_processor, so return true. */
+ trace (";%d GCany start.", self->id);
+ mutex_lock (&state_mutex);
+ if (gc_processor != NULL)
+ {
+#ifdef ENABLE_DEBUGGING_TOOLS
+ int GCer = gc_processor->id;
+#endif
+ trace (";%d GCany started by %d.", self->id, GCer);
+ gc_wait ();
+ trace (";%d GCany finished by %d.", self->id, GCer);
+ mutex_unlock (&state_mutex);
+ return (false);
+ }
+ gc_processor = self;
+ trace (";%d GCany initiated.", self->id);
+ self->state = PROCESSOR_GC_WAIT;
+ interrupt_others ();
+ export_state ();
+ if (! all_in (PROCESSOR_GC_WAIT))
+ {
+ trace (";%d GCany waiting.", self->id);
+ cond_wait (&ready, &state_mutex);
+ }
+ assert (all_in (PROCESSOR_GC_WAIT));
+ trace (";%d GCany ready (all waiting).", self->id);
+ mutex_unlock (&state_mutex);
+ return (true);
+}
+
+void
+smp_gc_finished (void)
+{
+ /* Called by the primitive gc daemon and primitive-fasdump to
+ broadcast the finished condition. */
+ trace (";%d smp_gc_finished", self->id);
+ mutex_lock (&state_mutex);
+ assert (gc_processor == self);
+ assert (all_in (PROCESSOR_GC_WAIT));
+ gc_processor = NULL;
+ {
+ processor_t *P = processors;
+ while (P != NULL)
+ {
+ if (P != self)
+ P->free_pointer = P->heap_start;
+ P->state = PROCESSOR_RUNNING;
+ P = P->next;
+ }
+ }
+ cond_broadcast (&finished);
+ mutex_unlock (&state_mutex);
+}
#endif
+\f
DEFINE_PRIMITIVE ("SMP-PAUSE", Prim_smp_pause, 0, 0, "(SMP-PAUSE)\n\
Pause a new processor.")
#endif
PRIMITIVE_RETURN (UNSPECIFIC);
}
+
+DEFINE_PRIMITIVE ("SMP-GC-WAIT", Prim_smp_gc_wait, 0, 0, "(SMP-GC-WAIT)\n\
+Put the current processor in the GC-WAIT state.\n\
+Called by the global-gc interrupt handler.")
+{
+ PRIMITIVE_HEADER (0);
+#ifdef ENABLE_SMP
+ trace (";%d SMP-GC-Wait.", self->id);
+ CLEAR_INTERRUPT (INT_Global_GC);
+ mutex_lock (&state_mutex);
+ gc_wait ();
+ mutex_unlock (&state_mutex);
+#else
+ signal_error_from_primitive (ERR_UNIMPLEMENTED_PRIMITIVE);
+#endif
+ PRIMITIVE_RETURN (UNSPECIFIC);
+}
+
+DEFINE_PRIMITIVE ("SMP-GC-FINISHED", Prim_smp_gc_finished, 0, 0,
+ "(SMP-GC-FINISHED)\n\
+Broadcast that the gc_finished condition variable is true.\n\
+Called at the end of the primitive GC daemon.")
+{
+ PRIMITIVE_HEADER (0);
+#ifdef ENABLE_SMP
+ trace (";%d SMP-GC-Finished.", self->id);
+ smp_gc_finished ();
+#endif
+ PRIMITIVE_RETURN (UNSPECIFIC);
+}