From 0063c621729a130fa031c393a39f12912df13d99 Mon Sep 17 00:00:00 2001 From: Jake Hughes Date: Tue, 16 Apr 2024 19:25:13 +0100 Subject: [PATCH 1/3] Move globals into BDWGC global array This is the canonical place for globals and ensures that they are included in the right object files. --- fnlz_mlc.c | 29 +++++++++++++---------------- include/gc/gc_disclaim.h | 11 ----------- include/private/gc_priv.h | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/fnlz_mlc.c b/fnlz_mlc.c index 110439d39..849f2e946 100644 --- a/fnlz_mlc.c +++ b/fnlz_mlc.c @@ -134,9 +134,6 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb, # ifdef BUFFERED_FINALIZATION -STATIC GC_finalization_buffer_hdr* start_buffer; -STATIC struct GC_current_buffer cur_buffer; - static void* init_finalize_thread(void *arg) { while (1) { @@ -176,28 +173,28 @@ STATIC int GC_CALLBACK GC_push_object_to_fin_buffer(void *obj) return 0; } - if (start_buffer == NULL) { + if (GC_finalizer_buffer_head == NULL) { /* This can happen for two reasons: * 1) This is first time a finalizable object is unreachable and no * finalization buffers have been created yet. * * 2) The buffer(s) have already passed to a finalization thread * which is processing them. We must start again. */ - start_buffer = GC_new_buffer(); - cur_buffer.hdr = start_buffer; - cur_buffer.cursor = (void**) (start_buffer + 1); + GC_finalizer_buffer_head = GC_new_buffer(); + GC_finalizer_buffer_current.hdr = GC_finalizer_buffer_head; + GC_finalizer_buffer_current.cursor = (void**) (GC_finalizer_buffer_head + 1); } - void** last = (void**) ((void *)cur_buffer.hdr + GC_page_size); - if (cur_buffer.cursor == last) { + void** last = (void**) ((void *)GC_finalizer_buffer_current.hdr + GC_page_size); + if (GC_finalizer_buffer_current.cursor == last) { GC_finalization_buffer_hdr* next = GC_new_buffer(); - cur_buffer.hdr->next = next; - cur_buffer.hdr = next; - cur_buffer.cursor = (void**) (next + 1); + GC_finalizer_buffer_current.hdr->next = next; + GC_finalizer_buffer_current.hdr = next; + GC_finalizer_buffer_current.cursor = (void**) (next + 1); } - *cur_buffer.cursor = obj; - cur_buffer.cursor++; + *GC_finalizer_buffer_current.cursor = obj; + GC_finalizer_buffer_current.cursor++; return 1; } @@ -256,8 +253,8 @@ GC_API void GC_CALL GC_finalize_objects(void) { * collection, so the finalisation thread will always load the up-to-date * version of this global. */ GC_disable(); - GC_finalization_buffer_hdr* buffer = start_buffer; - start_buffer = NULL; + GC_finalization_buffer_hdr* buffer = GC_finalizer_buffer_head; + GC_finalizer_buffer_head = NULL; GC_enable(); while(buffer != NULL) diff --git a/include/gc/gc_disclaim.h b/include/gc/gc_disclaim.h index 334008106..ebc6f16d1 100644 --- a/include/gc/gc_disclaim.h +++ b/include/gc/gc_disclaim.h @@ -69,17 +69,6 @@ GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL const struct GC_finalizer_closure * /* fc */) GC_ATTR_NONNULL(2); -typedef struct GC_finalization_buffer_hdr GC_finalization_buffer_hdr; - -struct GC_finalization_buffer_hdr { - GC_finalization_buffer_hdr* next; -}; - -struct GC_current_buffer { - GC_finalization_buffer_hdr* hdr; - void** cursor; -}; - /* This API is defined only if the library has been suitably compiled */ /* (i.e. with ENABLE_DISCLAIM defined). */ diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 2fcabefaf..7f5c452d6 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -324,6 +324,21 @@ typedef unsigned int unsigned32; #include "gc/gc_inline.h" +#ifdef BUFFERED_FINALIZATION + +typedef struct GC_finalization_buffer_hdr GC_finalization_buffer_hdr; + +struct GC_finalization_buffer_hdr { + GC_finalization_buffer_hdr* next; +}; + +struct GC_current_buffer { + GC_finalization_buffer_hdr* hdr; + void** cursor; +}; + +#endif + /*********************************/ /* */ /* Definitions for conservative */ @@ -1557,8 +1572,14 @@ struct _GC_arrays { # ifdef ENABLE_DISCLAIM # define GC_finalized_kind GC_arrays._finalized_kind unsigned _finalized_kind; +# ifdef BUFFERED_FINALIZATION # define GC_fin_q_kind GC_arrays._fin_q_kind unsigned _fin_q_kind; +# define GC_finalizer_buffer_head GC_arrays._fin_buffer_head + GC_finalization_buffer_hdr* _fin_buffer_head; +# define GC_finalizer_buffer_current GC_arrays._fin_buffer_current + struct GC_current_buffer _fin_buffer_current; +# endif # endif # define n_root_sets GC_arrays._n_root_sets # define GC_excl_table_entries GC_arrays._excl_table_entries From 1422e27ee09cf2fcf02e51d989652826b8682b31 Mon Sep 17 00:00:00 2001 From: Jake Hughes Date: Tue, 16 Apr 2024 19:29:22 +0100 Subject: [PATCH 2/3] Lazily spawn finalisation thread This ensures that the synchronisation cost of spawning the finalisation thread is only spent when a) the program contains finalisers, and b) dead objects become finalisable candidates. --- fnlz_mlc.c | 12 ++++++++++-- include/private/gc_priv.h | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/fnlz_mlc.c b/fnlz_mlc.c index 849f2e946..2ab918a3d 100644 --- a/fnlz_mlc.c +++ b/fnlz_mlc.c @@ -222,8 +222,6 @@ GC_API void GC_CALL GC_init_buffered_finalization(void) GC_register_disclaim_proc_inner(GC_fin_q_kind, GC_push_object_to_fin_buffer, TRUE); UNLOCK(); - pthread_t t; - pthread_create(&t, NULL, init_finalize_thread, NULL /* arg */); } void GC_finalize_buffer(GC_finalization_buffer_hdr* buffer) { @@ -267,6 +265,16 @@ GC_API void GC_CALL GC_finalize_objects(void) { } } +GC_INNER void GC_maybe_spawn_finalize_thread() +{ + if (GC_finalizer_thread_exists || !GC_finalizer_buffer_head) + return; + + pthread_t t; + pthread_create(&t, NULL, init_finalize_thread, NULL /* arg */); + GC_finalizer_thread_exists = 1; +} + # endif #endif /* ENABLE_DISCLAIM */ diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 7f5c452d6..6c6b199c3 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -337,6 +337,8 @@ struct GC_current_buffer { void** cursor; }; +GC_INNER void GC_maybe_spawn_finalize_thread(); + #endif /*********************************/ @@ -383,7 +385,11 @@ struct GC_current_buffer { EXTERN_C_BEGIN #ifndef GC_NO_FINALIZATION +#ifdef BUFFERED_FINALIZATION +# define GC_INVOKE_FINALIZERS() GC_maybe_spawn_finalize_thread() +#else # define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers() +#endif GC_INNER void GC_notify_or_invoke_finalizers(void); /* If GC_finalize_on_demand is not set, invoke */ /* eligible finalizers. Otherwise: */ @@ -1579,6 +1585,8 @@ struct _GC_arrays { GC_finalization_buffer_hdr* _fin_buffer_head; # define GC_finalizer_buffer_current GC_arrays._fin_buffer_current struct GC_current_buffer _fin_buffer_current; +# define GC_finalizer_thread_exists GC_arrays._fin_thread_exists + int _fin_thread_exists; # endif # endif # define n_root_sets GC_arrays._n_root_sets From fd7ce82add06a0d97c91fbd19a9df915997db943 Mon Sep 17 00:00:00 2001 From: Jake Hughes Date: Wed, 17 Apr 2024 10:11:10 +0100 Subject: [PATCH 3/3] Remove heap warnings when using buffered finalisation The allocation of pages for the finaliser buffer is done from within the STW pause. This can cause the heap to grow if lots of objects are enqueued for finalisation in a single GC and can cause spurious warnings to be printed to stderr. --- misc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc.c b/misc.c index 6a3b589d5..61f33577d 100644 --- a/misc.c +++ b/misc.c @@ -2069,9 +2069,11 @@ GC_API void GC_CALL GC_enable(void) LOCK(); GC_ASSERT(GC_dont_gc != 0); /* ensure no counter underflow */ GC_dont_gc--; +#ifndef BUFFERED_FINALIZATION if (!GC_dont_gc && GC_heapsize > GC_heapsize_on_gc_disable) WARN("Heap grown by %" WARN_PRIuPTR " KiB while GC was disabled\n", (GC_heapsize - GC_heapsize_on_gc_disable) >> 10); +#endif UNLOCK(); }