smp: Synchronize processors for GC.
authorMatt Birkholz <puck@birchwood-abbey.net>
Sat, 20 Dec 2014 15:32:10 +0000 (08:32 -0700)
committerMatt Birkholz <puck@birchwood-abbey.net>
Sun, 21 Dec 2014 19:19:08 +0000 (12:19 -0700)
src/microcode/fasdump.c
src/microcode/fasload.c
src/microcode/memmag.c
src/microcode/ossmp.h
src/microcode/prossmp.c
src/microcode/purify.c
src/microcode/uxsig.c
src/runtime/gcdemn.scm
src/runtime/intrpt.scm

index e04c5b7fab4ea2c542fe1e560bb526040ca22bf7..bbe4e9087bd47565a7c3731688ed60e56bfa0479 100644 (file)
@@ -108,9 +108,13 @@ static bool write_fasl_file
 #ifndef ENABLE_SMP
 #define HEAP_START heap_start
 #define HEAP_END heap_end
+#define SMP_GC_START()
+#define SMP_GC_FINISHED()
 #else
 #define HEAP_START shared_heap_start
 #define HEAP_END shared_heap_end
+#define SMP_GC_START smp_gc_start
+#define SMP_GC_FINISHED smp_gc_finished
 #endif
 \f
 /* FASDUMP:
@@ -149,6 +153,7 @@ at by compiled code are ignored (and discarded).")
     error_bad_range_arg (2);
   transaction_record_action (tat_always, close_fasl_file, (&ff_info));
 
+  SMP_GC_START ();
   open_tospace (HEAP_START);
   /* This must be _before_ the call to initialize_fixups(): */
   transaction_record_action (tat_abort, abort_fasdump, 0);
@@ -192,6 +197,7 @@ at by compiled code are ignored (and discarded).")
   ok = ((write_fasl_header (fh, (ff_info . handle)))
        && (save_tospace (save_tospace_write, (&ff_info))));
 
+  SMP_GC_FINISHED ();
   transaction_commit ();       /* 1 */
 
   COMPARE_GC_VARS ();
@@ -576,6 +582,7 @@ When the file is reloaded, PROCEDURE is called with an argument of #F.")
   to = Free;
   Primitive_GC_If_Needed (6);
 #else
+  smp_gc_start ();
 
   ENTER_CRITICAL_SECTION ("band dump");
 
index 75d6b72fabc25b58775fbea2bb1f3efc45ee7745..d9ede4e9cd432b0273a77be6d5d92457dee81e95 100644 (file)
@@ -202,6 +202,9 @@ can, however, be any file which can be loaded with BINARY-FASLOAD.")
 
   CHECK_ARG (1, STRING_P);
   canonicalize_primitive_context ();
+#ifdef ENABLE_SMP
+  smp_gc_start ();
+#endif
   result = (read_band_file (ARG_REF (1)));
 
   /* Reset implementation state parameters.  */
index 50db38bd1728d440d7c83f3e36dc7230cef1cfbe..01a68fdf7958ada53d8003e784d679defc46a7dd 100644 (file)
@@ -472,6 +472,13 @@ the primitive GC daemons before returning.")
 
 #else
 
+  while (true) {
+    if (smp_gc_started ())
+      break;
+    if (Free == heap_start)
+      PRIMITIVE_RETURN (ULONG_TO_FIXNUM (HEAP_AVAILABLE));
+  }
+
   open_tospace (shared_heap_start);
 
 #endif /* ENABLE_SMP */
index 5adef326ba45ffd6ddfbe89c62e6ef38b01883bf..449687caf30c145003ec3ac8d5e3d1eff332ff05 100644 (file)
@@ -35,6 +35,7 @@ typedef enum {
   PROCESSOR_NEW,
   PROCESSOR_RUNNING,
   PROCESSOR_PAUSED,
+  PROCESSOR_GC_WAIT,
   PROCESSOR_DEAD
 } processor_state_t;
 
@@ -61,6 +62,13 @@ extern processor_t *processors;
 extern __thread processor_t *self;
 
 extern void smp_initialize (int processor_count);
+
+extern void smp_gc_start (void);
+extern bool smp_gc_started (void);
+extern void smp_gc_finished (void);
+
+extern void smp_kill_gc (pthread_t);
+
 #endif
 
 #endif /* SCM_OSSMP_H */
index bfcbe5700605bd6982887a53809f757eea2e85ff..944224d463e9fb8539a3a414ab5b230ab8e39c70 100644 (file)
@@ -38,6 +38,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 = 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;
 
@@ -68,6 +81,61 @@ 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->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\
@@ -188,7 +256,7 @@ make_processors (int id)
   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);
     }
@@ -205,15 +273,7 @@ make_processors (int id)
   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);
@@ -223,7 +283,161 @@ make_processors (int id)
   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.")
@@ -239,3 +453,33 @@ 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);
+}
index a1744deba26a6aba40d058996a98f0a5ea2ce9a9..e4d6c11aefd6ad80448d080d8109b528c8e4ac07 100644 (file)
@@ -34,9 +34,11 @@ USA.
 #ifndef ENABLE_SMP
 #define HEAP_START heap_start
 #define HEAP_END heap_end
+#define SMP_GC_START()
 #else
 #define HEAP_START shared_heap_start
 #define HEAP_END shared_heap_end
+#define SMP_GC_START smp_gc_start
 #endif
 
 static void purify (SCHEME_OBJECT);
@@ -66,6 +68,7 @@ PURE? is ignored.")
 
   ENTER_CRITICAL_SECTION ("purify");
   heap_reserved = safety_margin;
+  SMP_GC_START ();
   purify (object);
 
  Will_Push (CONTINUATION_SIZE);
index 9722a91ddbc91c2f60f1e671f1ced589950394a9..5b6347c9d3898011a72737715cbb209a69dfa7da 100644 (file)
@@ -31,6 +31,7 @@ USA.
 #include "osctty.h"
 #include "ostty.h"
 #include "ostop.h"
+#include "ossmp.h"
 #include "uxtrap.h"
 #include "uxsig.h"
 #include "uxutil.h"
@@ -505,6 +506,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 (pthread_t thread)
+{
+  pthread_kill (thread, SIGUSR2);
+}
+
+#endif /* ENABLE_SMP */
 
 /* The following conditionalization would more naturally be expressed
    by conditionalizing the code inside the handler, but the Sun
@@ -671,8 +687,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
index 12e0e95d572c82d0cec20909e0743028457269b7..bb70cfa0cd30adac1b8ea87797ebbb13ccef2058 100644 (file)
@@ -31,7 +31,7 @@ USA.
 \f
 (define (initialize-package!)
   (set! primitive-gc-daemons (make-queue))
-  (set! trigger-primitive-gc-daemons! (make-trigger primitive-gc-daemons))
+  (set! %trigger-primitive-gc-daemons! (make-trigger primitive-gc-daemons))
   (set! add-primitive-gc-daemon! (make-adder primitive-gc-daemons))
   (set! gc-daemons (make-queue))
   (set! trigger-gc-daemons! (make-trigger gc-daemons))
@@ -47,9 +47,14 @@ USA.
 ;;; allocate any storage and they must be prepared to run at times
 ;;; when many data structures are not consistent.
 (define primitive-gc-daemons)
-(define trigger-primitive-gc-daemons!)
+(define %trigger-primitive-gc-daemons!)
 (define add-primitive-gc-daemon!)
 
+(define (trigger-primitive-gc-daemons!)
+  (%trigger-primitive-gc-daemons!)
+  (if ((ucode-primitive get-primitive-address 2) 'SMP-GC-FINISHED #f)
+      ((ucode-primitive smp-gc-finished 0))))
+
 ;;; GC-DAEMONS are executed after each GC from an interrupt handler.
 ;;; This interrupt handler has lower priority than the GC interrupt,
 ;;; which guarantees that these daemons will not be run inside of
index 866673bc01f1748c1ec9eceb5238db64e63397f4..f8eddfc66dddc2e5afda7a7af1ad7956f4305643 100644 (file)
@@ -123,6 +123,10 @@ USA.
   ;; prevent us from getting into a loop just running the daemons.
   (clear-interrupts! interrupt-bit/after-gc))
 
+(define (global-gc-interrupt-handler interrupt-code interrupt-enables)
+  interrupt-code interrupt-enables
+  ((ucode-primitive smp-gc-wait 0)))
+
 (define event:console-resize)
 (define (console-resize-handler interrupt-code interrupt-enables)
   interrupt-code interrupt-enables
@@ -230,8 +234,12 @@ USA.
                      interrupt-mask/none)
 
         (vector-set! interrupt-mask-vector gc-slot
-                     ;; interrupt-mask/none
-                     (fix:lsh 1 global-gc-slot))
+                     interrupt-mask/none)
+
+        (vector-set! system-interrupt-vector global-gc-slot
+                     global-gc-interrupt-handler)
+        (vector-set! interrupt-mask-vector global-gc-slot
+                     interrupt-mask/none)
 
         (vector-set! system-interrupt-vector timer-slot
                      timer-interrupt-handler)