Add SMP GC primitives and use them (with 1 processor).
authorMatt Birkholz <puck@birchwood-abbey.net>
Tue, 18 Aug 2015 07:11:23 +0000 (00:11 -0700)
committerMatt Birkholz <puck@birchwood-abbey.net>
Thu, 26 Nov 2015 08:09:44 +0000 (01:09 -0700)
Add SMP-GC-LOCK, SMP-GC-UNLOCK and SMP-GC-WAIT.  Add a stub for the
remaining unimplemented SMP primitive: SMP-LOCK-OBARRAY.  Make
SMP-COUNT return 1 so that the runtime system sets enable-smp? and
calls the new primitives.

Drop the threads_mutex (but keep threads_processor set) in SMP-GC-LOCK
so that processors waiting to enter the thread system (in
SMP-LOCK-THREADS) will wake, enter the GC-WAIT state, then try again.

src/microcode/fasdump.c
src/microcode/intern.c
src/microcode/memmag.c
src/microcode/ossmp.h
src/microcode/prossmp.c
src/microcode/purify.c
src/microcode/uxsig.c

index 6559509138d6cb518f04fde516cdfb2c6c542922..ed17031ec29b5d2d227796b3b281f84e7a04f879 100644 (file)
@@ -141,6 +141,9 @@ at by compiled code are ignored (and discarded).")
     error_bad_range_arg (2);
   transaction_record_action (tat_always, close_fasl_file, (&ff_info));
 
+#ifdef ENABLE_SMP
+  assert (gc_processor == self);
+#endif
   open_tospace (heap_start);
   /* This must be _before_ the call to initialize_fixups(): */
   transaction_record_action (tat_abort, abort_fasdump, 0);
index 21d29f5f7d8cad91653e73fa487ec0322e30659a..a4808a0355878ac574fdc7480e9a0cd8a9817a34 100644 (file)
@@ -265,3 +265,12 @@ Equivalent to (MODULO (STRING-HASH STRING) DENOMINATOR).")
                        % (arg_ulong_integer (2))));
   }
 }
+
+DEFINE_PRIMITIVE ("SMP-LOCK-OBARRAY", Prim_smp_lock_obarray, 1, 1,
+                 "(SMP-LOCK-OBARRAY LOCK?)\n\
+When LOCK? is #F/non-#F, unlock/lock the pthread mutex serializing\n\
+access to the obarray.  Value is #T unless there was an error.")
+{
+  PRIMITIVE_HEADER (1);
+  PRIMITIVE_RETURN (SHARP_T);
+}
index ae61b8c660f03615ed635b25fe92698f9c4a125a..afd406599a353232d8309b9f44b5974936cfb9ef 100644 (file)
@@ -270,6 +270,9 @@ the primitive GC daemons before returning.")
   if (GC_Debug == true) verify_heap ();
 #endif
 
+#ifdef ENABLE_SMP
+  assert (gc_processor == self);
+#endif
   open_tospace (heap_start);
   initialize_weak_chain ();
   ephemeron_count = 0;
index 2c0cd8e655bb9632ea37ced4131d354f9a59c4da..899046e0e862f96e29e1e93c3dfda17bb83959d8 100644 (file)
@@ -31,19 +31,31 @@ USA.
 
 #include <pthread.h>
 
+typedef enum {
+  PROCESSOR_NEW,
+  PROCESSOR_RUNNING,
+  PROCESSOR_IDLE,
+  PROCESSOR_GC_WAIT,
+  PROCESSOR_DEAD
+} processor_state_t;
+
 typedef struct processor processor_t;
 
 struct processor {
   struct processor *next;
   char id;
+  processor_state_t state;
   pthread_t pthread;
 };
 
 extern processor_t *processors;
 extern __thread processor_t *self;
+extern processor_t *gc_processor;
 
 extern void setup_processors (int count);
 
+extern void smp_kill_gc (processor_t *);
+
 #ifdef ENABLE_DEBUGGING_TOOLS
 
 extern bool smp_trace_p;
index c4dac31305877fb00e42fe53846f0a263cb98e0e..e5b7f8ef03d3eb8a1cff3cf154149728d5a2fe75 100644 (file)
@@ -36,6 +36,19 @@ USA.
 /* 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 = 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 the thread system. */
 static pthread_mutex_t threads_mutex = MUTEX_INITIALIZER;
 
@@ -47,7 +60,7 @@ __thread processor_t *self;
 
 #ifdef ENABLE_DEBUGGING_TOOLS
 
-bool smp_trace_p = true;
+bool smp_trace_p = false;
 
 static void
 trace (const char * format, ...)
@@ -65,6 +78,65 @@ trace (const char * format, ...)
 
 #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->state = PROCESSOR_DEAD;
+  pthread_exit ((void*)self);
+  /* NOTREACHED */
+}
+
+static void pthread_error (int code);
+
+static void
+mutex_lock (pthread_mutex_t *mutex)
+{
+  int err = pthread_mutex_lock (mutex);
+  if (err != 0)
+    {
+      pthread_error (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 != 0)
+    {
+      pthread_error (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 != 0)
+    {
+      pthread_error (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 != 0)
+    {
+      pthread_error (err);
+      fatal ("\n;%d pthread_cond_broadcast failed: %d", self->id, err);
+    }
+}
+
 static void
 pthread_error (int code)
 {
@@ -118,19 +190,71 @@ make_processors (int id)
     }
   new->next = processors;
   new->id = id;
+  new->state = PROCESSOR_NEW;
   processors = new;
 
   if (id > 0)
     make_processors (id - 1);
 }
 
-#endif /* ENABLE_SMP */
+static bool
+all_in (processor_state_t s)
+{
+  bool all = true;
+  for (processor_t *p = processors; p != NULL; p = p->next)
+    if (p->state != s)
+      {
+       all = false;
+       break;
+      }
+  return (all);
+}
+
+static void
+gc_wait (void)
+{
+  self->state = PROCESSOR_GC_WAIT;
+  if (all_in (PROCESSOR_GC_WAIT))
+    {
+      trace (";%d GC wait ready.", self->id);
+      cond_broadcast (&ready);
+    }
+  cond_wait (&finished, &state_mutex);
+  trace (";%d GC wait finished.", self->id);
 
+  assert (self->state == PROCESSOR_RUNNING);
+}
+
+static void
+interrupt_others (void)
+{
+  for (processor_t *p = processors; p != NULL; p = p->next)
+    if (p != self && p->state != PROCESSOR_GC_WAIT)
+      {
+       trace (";%d sending SIGUSR2 to %d", self->id, p->id);
+       smp_kill_gc (p);
+      }
+}
+
+static void
+smp_gc_wait (void)
+{
+  mutex_lock (&state_mutex);
+  CLEAR_INTERRUPT (INT_Global_GC);
+  gc_wait ();
+  mutex_unlock (&state_mutex);
+}
+#endif /* ENABLE_SMP */
+\f
 DEFINE_PRIMITIVE ("SMP-COUNT", Prim_smp_count, 0, 0, "(SMP-COUNT)\n\
 The number of concurrently running Symmetric Multi-Processors.")
 {
   PRIMITIVE_HEADER (0);
+#ifdef ENABLE_SMP
+  PRIMITIVE_RETURN (LONG_TO_FIXNUM (1));
+#else
   PRIMITIVE_RETURN (SHARP_F);
+#endif
 }
 
 DEFINE_PRIMITIVE ("SMP-ID", Prim_smp_id, 0, 0, "(SMP-ID)\n\
@@ -159,12 +283,94 @@ access to the thread system.")
       PRIMITIVE_RETURN (UNSPECIFIC);
     }
   else
-    {
-      mutex_lock (&threads_mutex);
-      threads_processor = self;
-      PRIMITIVE_RETURN (UNSPECIFIC);
-    }
+    while (true)
+      {
+       mutex_lock (&threads_mutex);
+       if (threads_processor == NULL)
+         {
+           threads_processor = self;
+           PRIMITIVE_RETURN (UNSPECIFIC);
+         }
+       else
+         {
+           assert (threads_processor != self);
+           trace (";%d SMP-Lock-Threads: direct to GC-Wait.", self->id);
+           mutex_unlock (&threads_mutex);
+           smp_gc_wait ();
+         }
+      }
 #else /* not ENABLE_SMP */
   PRIMITIVE_RETURN (UNSPECIFIC);
 #endif
 }
+
+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);
+  smp_gc_wait ();
+  trace (";%d SMP-GC-Wait done.", self->id);
+#else
+  signal_error_from_primitive (ERR_UNIMPLEMENTED_PRIMITIVE);
+#endif
+  PRIMITIVE_RETURN (UNSPECIFIC);
+}
+
+DEFINE_PRIMITIVE ("SMP-GC-LOCK", Prim_smp_gc_lock, 0, 0,
+                 "(SMP-GC-LOCK)\n\
+Lock the heap; call all other processors to the GC-WAIT state.  Value is #T\n\
+when successful and #F if some other processor has locked the heap.")
+{
+  PRIMITIVE_HEADER (0);
+#ifdef ENABLE_SMP
+  trace (";%d SMP-GC-Lock.", self->id);
+  mutex_lock (&state_mutex);
+  if (gc_processor != NULL)
+    {
+      trace (";%d SMP-GC-Lock lost to %d.", self->id, gc_processor->id);
+      mutex_unlock (&state_mutex);
+      PRIMITIVE_RETURN (SHARP_F);
+    }
+  gc_processor = self;
+  self->state = PROCESSOR_GC_WAIT;
+  if (self == threads_processor)
+    mutex_unlock (&threads_mutex);
+  interrupt_others ();
+  if (! all_in (PROCESSOR_GC_WAIT))
+    {
+      trace (";%d SMP-GC-Lock waiting.", self->id);
+      cond_wait (&ready, &state_mutex);
+    }
+  trace (";%d SMP-GC-Lock ready.", self->id);
+  mutex_unlock (&state_mutex);
+#else
+  signal_error_from_primitive (ERR_UNIMPLEMENTED_PRIMITIVE);
+#endif
+  PRIMITIVE_RETURN (SHARP_T);
+}
+
+DEFINE_PRIMITIVE ("SMP-GC-UNLOCK", Prim_smp_gc_unlock, 0, 0,
+                 "(SMP-GC-UNLOCK)\n\
+Release other processors from the GC-WAIT state.")
+{
+  PRIMITIVE_HEADER (0);
+#ifdef ENABLE_SMP
+  trace (";%d SMP-GC-Unlock.", self->id);
+  mutex_lock (&state_mutex);
+  assert (gc_processor == self);
+  assert (all_in (PROCESSOR_GC_WAIT));
+  gc_processor = NULL;
+  for (processor_t *p = processors; p != NULL; p = p->next)
+    p->state = PROCESSOR_RUNNING;
+  if (self == threads_processor)
+    mutex_lock (&threads_mutex);
+  mutex_unlock (&state_mutex);
+  cond_broadcast (&finished);
+#else
+  signal_error_from_primitive (ERR_UNIMPLEMENTED_PRIMITIVE);
+#endif
+  PRIMITIVE_RETURN (UNSPECIFIC);
+}
index fc25c42a95faa8389fb0ecc8964acc9389970a78..03a66cb668be35b15440c5a7cfa06a5b8dd28047 100644 (file)
@@ -56,6 +56,9 @@ PURE? is ignored.")
   POP_PRIMITIVE_FRAME (3);
 
   ENTER_CRITICAL_SECTION ("purify");
+#ifdef ENABLE_SMP
+  assert (gc_processor == self);
+#endif
   heap_reserved = safety_margin;
   purify (object);
 
index 952ddcaea40c26048bb18f7d4f4a2257bdb5bf5c..2f675f4c5617dd51ea9bd7235a9677bd4922594c 100644 (file)
@@ -505,6 +505,21 @@ DEFUN_STD_HANDLER (sighnd_console_resize,
   request_console_resize_interrupt ();
 })
 
+#ifdef ENABLE_SMP
+
+static
+DEFUN_STD_HANDLER (sighnd_global_gc,
+{
+  REQUEST_INTERRUPT (INT_Global_GC);
+})
+
+void
+smp_kill_gc (processor_t *p)
+{
+  pthread_kill (p->pthread, SIGUSR2);
+}
+
+#endif /* ENABLE_SMP */
 
 /* The following conditionalization would more naturally be expressed
    by conditionalizing the code inside the handler, but the Sun
@@ -671,8 +686,12 @@ UX_initialize_signals (void)
   bind_handler (SIGALRM,       sighnd_timer);
   bind_handler (SIGVTALRM,     sighnd_timer);
   bind_handler (SIGUSR1,       sighnd_save_then_terminate);
+#ifndef ENABLE_SMP
 #ifdef HAVE_NICE
   bind_handler (SIGUSR2,       sighnd_renice);
+#endif
+#else
+  bind_handler (SIGUSR2,       sighnd_global_gc);
 #endif
   bind_handler (SIGCHLD,       sighnd_dead_subprocess);
   /* If this signal is ignored, then the system call that would have