wingo pushed a commit to branch wip-whippet
in repository guile.

commit 8fba0e53223fd432b7f3968e53d58860fa8cbf7d
Author: Andy Wingo <wi...@igalia.com>
AuthorDate: Wed Sep 18 11:31:06 2024 +0200

    Implement cooperative safepoint API
    
    Fixes https://github.com/wingo/whippet/issues/9.
---
 api/bdw-attrs.h  |  4 ++++
 api/gc-api.h     | 20 ++++++++++++++++++++
 api/gc-attrs.h   |  7 +++++++
 api/mmc-attrs.h  |  4 ++++
 api/pcc-attrs.h  |  4 ++++
 api/semi-attrs.h |  4 ++++
 doc/manual.md    | 17 ++++++++++++-----
 src/bdw.c        |  3 +++
 src/mmc.c        | 50 +++++++++++++++++++-------------------------------
 src/pcc.c        | 42 +++++++++++++++---------------------------
 src/semi.c       |  3 +++
 11 files changed, 95 insertions(+), 63 deletions(-)

diff --git a/api/bdw-attrs.h b/api/bdw-attrs.h
index 85c3aa250..af1042af3 100644
--- a/api/bdw-attrs.h
+++ b/api/bdw-attrs.h
@@ -54,6 +54,10 @@ static inline enum gc_safepoint_mechanism 
gc_safepoint_mechanism(void) {
   return GC_SAFEPOINT_MECHANISM_SIGNAL;
 }
 
+static inline enum gc_cooperative_safepoint_kind 
gc_cooperative_safepoint_kind(void) {
+  return GC_COOPERATIVE_SAFEPOINT_NONE;
+}
+
 static inline int gc_can_pin_objects(void) {
   return 1;
 }
diff --git a/api/gc-api.h b/api/gc-api.h
index 071655bff..63921f628 100644
--- a/api/gc-api.h
+++ b/api/gc-api.h
@@ -208,4 +208,24 @@ static inline void gc_write_barrier(struct gc_ref obj, 
size_t obj_size,
 
 GC_API_ void gc_pin_object(struct gc_mutator *mut, struct gc_ref obj);
 
+GC_API_ void gc_safepoint_slow(struct gc_mutator *mut) GC_NEVER_INLINE;
+GC_API_ int* gc_safepoint_flag_loc(struct gc_mutator *mut);
+static inline int gc_should_stop_for_safepoint(struct gc_mutator *mut) {
+  switch (gc_cooperative_safepoint_kind()) {
+  case GC_COOPERATIVE_SAFEPOINT_NONE:
+    return 0;
+  case GC_COOPERATIVE_SAFEPOINT_MUTATOR_FLAG:
+  case GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG: {
+    return atomic_load_explicit(gc_safepoint_flag_loc(mut),
+                                memory_order_relaxed);
+  }
+  default:
+    GC_CRASH();
+  }
+}
+static inline void gc_safepoint(struct gc_mutator *mut) {
+  if (GC_UNLIKELY(gc_should_stop_for_safepoint(mut)))
+    gc_safepoint_slow(mut);
+}
+
 #endif // GC_API_H_
diff --git a/api/gc-attrs.h b/api/gc-attrs.h
index 8e914e361..b6acb4302 100644
--- a/api/gc-attrs.h
+++ b/api/gc-attrs.h
@@ -43,6 +43,13 @@ enum gc_safepoint_mechanism {
 };
 static inline enum gc_safepoint_mechanism gc_safepoint_mechanism(void) 
GC_ALWAYS_INLINE;
 
+enum gc_cooperative_safepoint_kind {
+  GC_COOPERATIVE_SAFEPOINT_NONE,
+  GC_COOPERATIVE_SAFEPOINT_MUTATOR_FLAG,
+  GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG,
+};
+static inline enum gc_cooperative_safepoint_kind 
gc_cooperative_safepoint_kind(void) GC_ALWAYS_INLINE;
+
 static inline int gc_can_pin_objects(void) GC_ALWAYS_INLINE;
 
 #endif // GC_ATTRS_H
diff --git a/api/mmc-attrs.h b/api/mmc-attrs.h
index d56b3d88c..e5757f6d1 100644
--- a/api/mmc-attrs.h
+++ b/api/mmc-attrs.h
@@ -61,6 +61,10 @@ static inline enum gc_safepoint_mechanism 
gc_safepoint_mechanism(void) {
   return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
 }
 
+static inline enum gc_cooperative_safepoint_kind 
gc_cooperative_safepoint_kind(void) {
+  return GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG;
+}
+
 static inline int gc_can_pin_objects(void) {
   return 1;
 }
diff --git a/api/pcc-attrs.h b/api/pcc-attrs.h
index b8b42baf0..2f02640ea 100644
--- a/api/pcc-attrs.h
+++ b/api/pcc-attrs.h
@@ -57,6 +57,10 @@ static inline enum gc_safepoint_mechanism 
gc_safepoint_mechanism(void) {
   return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
 }
 
+static inline enum gc_cooperative_safepoint_kind 
gc_cooperative_safepoint_kind(void) {
+  return GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG;
+}
+
 static inline int gc_can_pin_objects(void) {
   return 0;
 }
diff --git a/api/semi-attrs.h b/api/semi-attrs.h
index 3e0511074..bcd8e89e0 100644
--- a/api/semi-attrs.h
+++ b/api/semi-attrs.h
@@ -56,6 +56,10 @@ static inline enum gc_safepoint_mechanism 
gc_safepoint_mechanism(void) {
   return GC_SAFEPOINT_MECHANISM_COOPERATIVE;
 }
 
+static inline enum gc_cooperative_safepoint_kind 
gc_cooperative_safepoint_kind(void) {
+  return GC_COOPERATIVE_SAFEPOINT_NONE;
+}
+
 static inline int gc_can_pin_objects(void) {
   return 0;
 }
diff --git a/doc/manual.md b/doc/manual.md
index d2878ebd6..7c784b626 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -531,12 +531,19 @@ temporarily mark itself as inactive by trampolining 
through
 for, for example, system calls that might block.  Periodic safepoints is
 better for code that is active but not allocating.
 
-Thing is, though, `gc_safepoint` is not yet implemented :)  It will be,
-though!
-
 Also, the BDW collector actually uses pre-emptive safepoints: it stops
-threads via POSIX signals.  `gc_safepoint` is (or will be) a no-op with
-BDW.
+threads via POSIX signals.  `gc_safepoint` is a no-op with BDW.
+
+Embedders can inline safepoint checks.  If
+`gc_cooperative_safepoint_kind()` is `GC_COOPERATIVE_SAFEPOINT_NONE`,
+then the collector doesn't need safepoints, as is the case for `bdw`
+which uses signals and `semi` which is single-threaded.  If it is
+`GC_COOPERATIVE_SAFEPOINT_HEAP_FLAG`, then calling
+`gc_safepoint_flag_loc` on a mutator will return the address of an `int`
+in memory, which if nonzero when loaded using relaxed atomics indicates
+that the mutator should call `gc_safepoint_slow`.  Similarly for
+`GC_COOPERATIVE_SAFEPOINT_MUTATOR_FLAG`, except that the address is
+per-mutator rather than global.
 
 ### Pinning
 
diff --git a/src/bdw.c b/src/bdw.c
index e5394427a..df579dde7 100644
--- a/src/bdw.c
+++ b/src/bdw.c
@@ -153,6 +153,9 @@ void gc_write_barrier_extern(struct gc_ref obj, size_t 
obj_size,
                              struct gc_edge edge, struct gc_ref new_val) {
 }
 
+int* gc_safepoint_flag_loc(struct gc_mutator *mut) { GC_CRASH(); }
+void gc_safepoint_slow(struct gc_mutator *mut) { GC_CRASH(); }
+
 struct bdw_mark_state {
   struct GC_ms_entry *mark_stack_ptr;
   struct GC_ms_entry *mark_stack_limit;
diff --git a/src/mmc.c b/src/mmc.c
index 97091e9b6..eebc61903 100644
--- a/src/mmc.c
+++ b/src/mmc.c
@@ -417,6 +417,7 @@ static enum gc_collection_kind
 pause_mutator_for_collection(struct gc_heap *heap, struct gc_mutator *mut) {
   GC_ASSERT(mutators_are_stopping(heap));
   GC_ASSERT(!all_mutators_stopped(heap));
+  MUTATOR_EVENT(mut, mutator_stopping);
   MUTATOR_EVENT(mut, mutator_stopped);
   heap->paused_mutator_count++;
   enum gc_collection_kind collection_kind = heap->gc_kind;
@@ -432,35 +433,6 @@ pause_mutator_for_collection(struct gc_heap *heap, struct 
gc_mutator *mut) {
   return collection_kind;
 }
 
-static enum gc_collection_kind
-pause_mutator_for_collection_with_lock(struct gc_mutator *mut) GC_NEVER_INLINE;
-static enum gc_collection_kind
-pause_mutator_for_collection_with_lock(struct gc_mutator *mut) {
-  struct gc_heap *heap = mutator_heap(mut);
-  GC_ASSERT(mutators_are_stopping(heap));
-  MUTATOR_EVENT(mut, mutator_stopping);
-  return pause_mutator_for_collection(heap, mut);
-}
-
-static void pause_mutator_for_collection_without_lock(struct gc_mutator *mut) 
GC_NEVER_INLINE;
-static void
-pause_mutator_for_collection_without_lock(struct gc_mutator *mut) {
-  struct gc_heap *heap = mutator_heap(mut);
-  GC_ASSERT(mutators_are_stopping(heap));
-  MUTATOR_EVENT(mut, mutator_stopping);
-  nofl_finish_sweeping(&mut->allocator, heap_nofl_space(heap));
-  gc_stack_capture_hot(&mut->stack);
-  heap_lock(heap);
-  pause_mutator_for_collection(heap, mut);
-  heap_unlock(heap);
-}
-
-static inline void
-maybe_pause_mutator_for_collection(struct gc_mutator *mut) {
-  while (mutators_are_stopping(mutator_heap(mut)))
-    pause_mutator_for_collection_without_lock(mut);
-}
-
 static void
 resize_heap(struct gc_heap *heap, size_t new_size) {
   if (new_size == heap->size)
@@ -818,7 +790,7 @@ trigger_collection(struct gc_mutator *mut,
   nofl_allocator_finish(&mut->allocator, heap_nofl_space(heap));
   heap_lock(heap);
   while (mutators_are_stopping(heap))
-    prev_kind = pause_mutator_for_collection_with_lock(mut);
+    prev_kind = pause_mutator_for_collection(heap, mut);
   if (prev_kind < (int)requested_kind)
     collect(mut, requested_kind);
   heap_unlock(heap);
@@ -829,6 +801,22 @@ gc_collect(struct gc_mutator *mut, enum gc_collection_kind 
kind) {
   trigger_collection(mut, kind);
 }
 
+int*
+gc_safepoint_flag_loc(struct gc_mutator *mut) {
+  return &mutator_heap(mut)->collecting;
+}
+
+void
+gc_safepoint_slow(struct gc_mutator *mut) {
+  struct gc_heap *heap = mutator_heap(mut);
+  gc_stack_capture_hot(&mut->stack);
+  nofl_allocator_finish(&mut->allocator, heap_nofl_space(heap));
+  heap_lock(heap);
+  while (mutators_are_stopping(mutator_heap(mut)))
+    pause_mutator_for_collection(heap, mut);
+  heap_unlock(heap);
+}
+
 static void*
 allocate_large(struct gc_mutator *mut, size_t size) {
   struct gc_heap *heap = mutator_heap(mut);
@@ -894,7 +882,7 @@ gc_write_barrier_extern(struct gc_ref obj, size_t obj_size,
   GC_ASSERT(obj_size > gc_allocator_large_threshold());
   gc_object_set_remembered(obj);
 }
-
+  
 struct gc_ephemeron*
 gc_allocate_ephemeron(struct gc_mutator *mut) {
   struct gc_ref ret =
diff --git a/src/pcc.c b/src/pcc.c
index a99e2a967..90194e96a 100644
--- a/src/pcc.c
+++ b/src/pcc.c
@@ -283,6 +283,7 @@ static void
 pause_mutator_for_collection(struct gc_heap *heap, struct gc_mutator *mut) {
   GC_ASSERT(mutators_are_stopping(heap));
   GC_ASSERT(!all_mutators_stopped(heap));
+  MUTATOR_EVENT(mut, mutator_stopping);
   MUTATOR_EVENT(mut, mutator_stopped);
   heap->paused_mutator_count++;
   if (all_mutators_stopped(heap))
@@ -296,32 +297,6 @@ pause_mutator_for_collection(struct gc_heap *heap, struct 
gc_mutator *mut) {
   MUTATOR_EVENT(mut, mutator_restarted);
 }
 
-static void
-pause_mutator_for_collection_with_lock(struct gc_mutator *mut) GC_NEVER_INLINE;
-static void
-pause_mutator_for_collection_with_lock(struct gc_mutator *mut) {
-  struct gc_heap *heap = mutator_heap(mut);
-  GC_ASSERT(mutators_are_stopping(heap));
-  MUTATOR_EVENT(mut, mutator_stopping);
-  pause_mutator_for_collection(heap, mut);
-}
-
-static void pause_mutator_for_collection_without_lock(struct gc_mutator *mut) 
GC_NEVER_INLINE;
-static void pause_mutator_for_collection_without_lock(struct gc_mutator *mut) {
-  struct gc_heap *heap = mutator_heap(mut);
-  GC_ASSERT(mutators_are_stopping(heap));
-  MUTATOR_EVENT(mut, mutator_stopping);
-  copy_space_allocator_finish(&mut->allocator, heap_copy_space(heap));
-  heap_lock(heap);
-  pause_mutator_for_collection(heap, mut);
-  heap_unlock(heap);
-}
-
-static inline void maybe_pause_mutator_for_collection(struct gc_mutator *mut) {
-  while (mutators_are_stopping(mutator_heap(mut)))
-    pause_mutator_for_collection_without_lock(mut);
-}
-
 static void resize_heap(struct gc_heap *heap, size_t new_size) {
   if (new_size == heap->size)
     return;
@@ -447,7 +422,7 @@ static void trigger_collection(struct gc_mutator *mut) {
   heap_lock(heap);
   long epoch = heap->count;
   while (mutators_are_stopping(heap))
-    pause_mutator_for_collection_with_lock(mut);
+    pause_mutator_for_collection(heap, mut);
   if (epoch == heap->count)
     collect(mut);
   heap_unlock(heap);
@@ -512,6 +487,19 @@ void gc_write_barrier_extern(struct gc_ref obj, size_t 
obj_size,
                              struct gc_edge edge, struct gc_ref new_val) {
 }
 
+int* gc_safepoint_flag_loc(struct gc_mutator *mut) {
+  return &mutator_heap(mut)->collecting;
+}
+
+void gc_safepoint_slow(struct gc_mutator *mut) {
+  struct gc_heap *heap = mutator_heap(mut);
+  copy_space_allocator_finish(&mut->allocator, heap_copy_space(heap));
+  heap_lock(heap);
+  while (mutators_are_stopping(mutator_heap(mut)))
+    pause_mutator_for_collection(heap, mut);
+  heap_unlock(heap);
+}
+  
 struct gc_ephemeron* gc_allocate_ephemeron(struct gc_mutator *mut) {
   return gc_allocate(mut, gc_ephemeron_size());
 }
diff --git a/src/semi.c b/src/semi.c
index 84493e81c..7958b5898 100644
--- a/src/semi.c
+++ b/src/semi.c
@@ -457,6 +457,9 @@ void gc_write_barrier_extern(struct gc_ref obj, size_t 
obj_size,
                              struct gc_edge edge, struct gc_ref new_val) {
 }
 
+int* gc_safepoint_flag_loc(struct gc_mutator *mut) { GC_CRASH(); }
+void gc_safepoint_slow(struct gc_mutator *mut) { GC_CRASH(); }
+  
 static void collect_for_large_alloc(struct gc_mutator *mut, size_t npages) {
   collect_for_alloc(mut, npages * mutator_semi_space(mut)->page_size);
 }

Reply via email to