Skip to content

Commit

Permalink
Merge pull request #22 from jacob-hughes/switch_to_gc_finalize_api
Browse files Browse the repository at this point in the history
Switch to using the `GC_finalize` API for off-thread finalisation
  • Loading branch information
ltratt committed Aug 22, 2024
2 parents 53551d5 + 43d97f6 commit 74bb8e4
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 328 deletions.
6 changes: 0 additions & 6 deletions .buildbot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,3 @@ sh rustup.sh --default-host x86_64-unknown-linux-gnu \
--profile minimal \
-y
export PATH=`pwd`/.cargo/bin/:$PATH
BDWGC_SRC=`pwd`

git clone https://github.com/softdevteam/alloy
cd alloy
BDWGC=${BDWGC_SRC} ENABLE_GC_ASSERTIONS=true /usr/bin/time -v python3 x.py test --stage 2 \
--config .buildbot.config.toml --exclude rustdoc-json --exclude debuginfo
12 changes: 0 additions & 12 deletions alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1861,15 +1861,3 @@ GC_INNER ptr_t GC_allocobj(size_t lg, int k)
GC_fail_count = 0;
return (ptr_t)(*flh);
}

STATIC __thread void* tls_rootset = NULL;

GC_INNER void GC_init_tls_rootset(void * rootset)
{
tls_rootset = rootset;
}

GC_INNER void* GC_tls_rootset()
{
return tls_rootset;
}
196 changes: 0 additions & 196 deletions fnlz_mlc.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,200 +131,4 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb,
return (word *)op + 1;
}

# ifdef BUFFERED_FINALIZATION

static void* init_finalize_thread(void *arg)
{
while (1) {
GC_finalize_objects();
}
return arg;
}

GC_finalization_buffer_hdr* GC_new_buffer() {
GC_ASSERT(I_HOLD_LOCK());
void* ptr = GC_os_get_mem(GC_page_size);
if (NULL == ptr)
ABORT("Insufficient memory for finalization buffer.");
GC_add_roots_inner(ptr, ptr + GC_page_size, FALSE);
return ptr;
}

void GC_delete_buffer(GC_finalization_buffer_hdr* buffer) {
GC_remove_roots((void*) buffer, (void*) buffer + GC_page_size);
GC_unmap((void*)buffer, GC_page_size);

}

STATIC int GC_CALLBACK GC_push_object_to_fin_buffer(void *obj)
{
GC_ASSERT(I_HOLD_LOCK());

word finalizer_word = *(word *)obj;
if ((finalizer_word & FINALIZER_CLOSURE_FLAG) == 0) {
return 0;
}

if (finalizer_word & HAS_BEEN_FINALIZED_FLAG) {
/* The object has already been finalized. Return 0 ensures it is
* immediately reclaimed.
*/
return 0;
}

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. */
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 *)GC_finalizer_buffer_current.hdr + GC_page_size);
if (GC_finalizer_buffer_current.cursor == last) {
GC_finalization_buffer_hdr* next = GC_new_buffer();
GC_finalizer_buffer_current.hdr->next = next;
GC_finalizer_buffer_current.hdr = next;
GC_finalizer_buffer_current.cursor = (void**) (next + 1);
}

*GC_finalizer_buffer_current.cursor = obj;
GC_finalizer_buffer_current.cursor++;

return 1;
}


GC_API GC_ATTR_MALLOC void * GC_CALL GC_buffered_finalize_malloc(size_t lb)
{
void *op;

GC_ASSERT(GC_fin_q_kind != 0);
op = GC_malloc_kind(lb, (int)GC_fin_q_kind);
if (EXPECT(NULL == op, FALSE))
return NULL;
return (word *)op;
}

GC_API GC_ATTR_MALLOC void * GC_CALL GC_buffered_finalize_memalign(size_t align, size_t lb)
{
size_t offset;
ptr_t result;
size_t align_m1 = align - 1;

/* Check the alignment argument. */
if (EXPECT(0 == align || (align & align_m1) != 0, FALSE)) return NULL;
if (align <= GC_GRANULE_BYTES) return GC_buffered_finalize_malloc(lb);

if (align >= HBLKSIZE/2 || lb >= HBLKSIZE/2) {
return GC_clear_stack(GC_generic_malloc_aligned(lb, GC_fin_q_kind,
0 /* flags */, align_m1));
}

result = (ptr_t)GC_buffered_finalize_malloc(SIZET_SAT_ADD(lb, align_m1));
offset = (size_t)(word)result & align_m1;
if (offset != 0) {
offset = align - offset;
if (!GC_all_interior_pointers) {
GC_STATIC_ASSERT(VALID_OFFSET_SZ <= HBLKSIZE);
GC_ASSERT(offset < VALID_OFFSET_SZ);
GC_register_displacement(offset);
}
result += offset;
}
GC_ASSERT(((word)result & align_m1) == 0);
return result;
}

GC_API int GC_CALL GC_buffered_finalize_posix_memalign(void **memptr, size_t align, size_t lb)
{
size_t align_minus_one = align - 1; /* to workaround a cppcheck warning */

/* Check alignment properly. */
if (EXPECT(align < sizeof(void *)
|| (align_minus_one & align) != 0, FALSE)) {
return EINVAL;
}

if ((*memptr = GC_buffered_finalize_memalign(align, lb)) == NULL) {
return ENOMEM;
}
return 0;
}


GC_API void GC_CALL GC_init_buffered_finalization(void)
{
LOCK();
GC_new_buffer();
GC_fin_q_kind = GC_new_kind_inner(GC_new_free_list_inner(),
GC_DS_LENGTH, TRUE, TRUE);
GC_ASSERT(GC_fin_q_kind != 0);

GC_register_disclaim_proc_inner(GC_fin_q_kind, GC_push_object_to_fin_buffer, TRUE);
UNLOCK();
}

void GC_finalize_buffer(GC_finalization_buffer_hdr* buffer) {
void** cursor = (void**) (buffer + 1);
void** last = (void**) ((void *)buffer + GC_page_size);
while (cursor != last)
{
if (*cursor == NULL) {
break;
}
void* obj = *cursor;
word finalizer_word = (*(word *)obj) & ~(word)FINALIZER_CLOSURE_FLAG;
GC_disclaim_proc finalizer = (GC_disclaim_proc) finalizer_word;
(finalizer)(obj);
GC_num_finalized++;
/* Prevent the object from being re-added to the finalization queue */
*(word *)obj = finalizer_word | HAS_BEEN_FINALIZED_FLAG;
cursor++;
}
}

GC_API void GC_CALL GC_finalize_objects(void) {
/* This is safe to do without locking because this global is only ever
* mutated from within a collection where all mutator threads (including
* this finalisation thread) are paused. It is not possible for them to race.
*
* In addition, a memory barrier synchronises threads at the end of a
* collection, so the finalisation thread will always load the up-to-date
* version of this global. */
GC_disable();
GC_finalization_buffer_hdr* buffer = GC_finalizer_buffer_head;
GC_finalizer_buffer_head = NULL;
GC_enable();

while(buffer != NULL)
{
GC_finalize_buffer(buffer);
GC_finalization_buffer_hdr* next = buffer->next;

GC_delete_buffer(buffer);
buffer = next;
}
}

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;
}

GC_API size_t GC_finalized_total(void) {
return GC_num_finalized;
}

# endif

#endif /* ENABLE_DISCLAIM */
3 changes: 0 additions & 3 deletions include/gc/gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2358,7 +2358,4 @@ GC_API void GC_CALL GC_win32_free_heap(void);
} /* extern "C" */
#endif

GC_API void GC_init_tls_rootset(void * rootset);
GC_API void * GC_tls_rootset();

#endif /* GC_H */
39 changes: 0 additions & 39 deletions include/gc/gc_disclaim.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,45 +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);


#ifdef BUFFERED_FINALIZATION
/* This API is defined only if the library has been suitably compiled */
/* (i.e. with ENABLE_DISCLAIM defined). */

/* Prepare the object kind used by GC_buffered_finalize_malloc. Call */
/* it from your initialization code or, at least, at some point before */
/* finalized allocations. The function is thread-safe. */
GC_API void GC_CALL GC_init_buffered_finalization(void);

/* Allocate an object which is to be finalized. */
/* This function assumes the first word in the allocated block will */
/* store the function pointer to the finalizer. */
/* Allocations of this kind are added to a buffer which, when full, is */
/* passed to a user supplied closure to invoke their finalizers. It is */
/* the responsibility of the user to ensure these objects are finalized.*/
/* This uses a dedicated object kind with a disclaim procedure, and is */
/* more efficient than GC_register_finalizer and friends. */
/* GC_init_buffered_finalization must be called before using this. */
/* The collector will not reclaim the object during the GC cycle where */
/* it was considered unreachable. In addition, objects reachable from */
/* the finalizer will be protected from collection until the finalizer */
/* has been run. */
/* The disclaim procedure is not invoked in the leak-finding mode. */
/* There is no debugging version of this allocation API. */
GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
GC_buffered_finalize_malloc(size_t);

GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2) void * GC_CALL
GC_buffered_finalize_memalign(size_t /* align */, size_t /* lb */);

GC_API int GC_CALL GC_buffered_finalize_posix_memalign(void ** /* memptr */, size_t /* align */,
size_t /* lb */) GC_ATTR_NONNULL(1);

GC_API void GC_CALL GC_finalize_objects(void);

GC_API size_t GC_finalized_total(void);

#endif

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
37 changes: 4 additions & 33 deletions include/private/gc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,22 +341,7 @@ typedef struct hblkhdr hdr;

#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;
};

GC_INNER void GC_maybe_spawn_finalize_thread();

#endif
GC_INNER void GC_maybe_wake_finalizer_thread();

/*********************************/
/* */
Expand Down Expand Up @@ -402,11 +387,7 @@ GC_INNER void GC_maybe_spawn_finalize_thread();
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
# define GC_INVOKE_FINALIZERS() GC_maybe_wake_finalizer_thread()
GC_INNER void GC_notify_or_invoke_finalizers(void);
/* If GC_finalize_on_demand is not set, invoke */
/* eligible finalizers. Otherwise: */
Expand Down Expand Up @@ -1597,21 +1578,11 @@ struct _GC_arrays {
# define GC_trace_buf_ptr GC_arrays._trace_buf_ptr
int _trace_buf_ptr;
# endif
# define GC_finalizer_thread_exists GC_arrays._fin_thread_exists
int _fin_thread_exists;
# 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;
# define GC_finalizer_thread_exists GC_arrays._fin_thread_exists
int _fin_thread_exists;
# define GC_num_finalized GC_arrays._fin_total
unsigned _fin_total;
# endif
# endif
# define n_root_sets GC_arrays._n_root_sets
# define GC_excl_table_entries GC_arrays._excl_table_entries
Expand Down
8 changes: 0 additions & 8 deletions misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1481,9 +1481,6 @@ GC_API void GC_CALL GC_init(void)
GC_ASSERT(GC_bytes_allocd + GC_bytes_allocd_before_gc == 0);
# endif

# ifdef BUFFERED_FINALIZATION
GC_init_buffered_finalization();
# endif
}

GC_API void GC_CALL GC_enable_incremental(void)
Expand Down Expand Up @@ -2136,11 +2133,6 @@ 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();
}

Expand Down
2 changes: 0 additions & 2 deletions pthread_stop_world.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context)
}
crtn = me -> crtn;
GC_store_stack_ptr(crtn);
crtn -> tls_rootset = GC_tls_rootset();
get_thread_local_roots(&tlr);
crtn -> compiler_thread_roots = tlr;

Expand Down Expand Up @@ -834,7 +833,6 @@ GC_INNER void GC_push_all_stacks(void)
if (me != NULL) {
struct GC_ThreadLocalRoots tlr;
get_thread_local_roots(&tlr);
me -> crtn -> tls_rootset = GC_tls_rootset();
me -> crtn -> compiler_thread_roots = tlr;
}

Expand Down
Loading

0 comments on commit 74bb8e4

Please sign in to comment.