Skip to content

Commit

Permalink
Merge pull request #15 from jacob-hughes/buffered_finalization
Browse files Browse the repository at this point in the history
Add malloc calls for buffered finalization.
  • Loading branch information
ltratt authored Apr 15, 2024
2 parents 216d7de + 29d65b9 commit a77a4f9
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 0 deletions.
124 changes: 124 additions & 0 deletions fnlz_mlc.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,128 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb,
return (word *)op + 1;
}

# ifdef BUFFERED_FINALIZATION

STATIC GC_finalization_buffer_hdr* start_buffer;
STATIC struct GC_current_buffer cur_buffer;

GC_finalization_buffer_hdr* GC_new_buffer() {
GC_ASSERT(I_HOLD_LOCK());
GC_disable();
void* ptr = GC_os_get_mem(GC_page_size);
GC_enable();
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_ASSERT(I_HOLD_LOCK());
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 (start_buffer == 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);
}

void** last = (void**) ((void *)cur_buffer.hdr + GC_page_size);
if (cur_buffer.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);
}

*cur_buffer.cursor = obj;
cur_buffer.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 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);
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 = start_buffer;
start_buffer = NULL;
GC_enable();

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

GC_delete_buffer(buffer);
buffer = next;
}
}

# endif

#endif /* ENABLE_DISCLAIM */
40 changes: 40 additions & 0 deletions include/gc/gc_disclaim.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,46 @@ GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1) void * GC_CALL
GC_finalized_malloc(size_t /* size */,
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). */

/* 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 void GC_CALL GC_finalize_objects(void);

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
2 changes: 2 additions & 0 deletions include/private/gc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,8 @@ struct _GC_arrays {
# ifdef ENABLE_DISCLAIM
# define GC_finalized_kind GC_arrays._finalized_kind
unsigned _finalized_kind;
# define GC_fin_q_kind GC_arrays._fin_q_kind
unsigned _fin_q_kind;
# endif
# define n_root_sets GC_arrays._n_root_sets
# define GC_excl_table_entries GC_arrays._excl_table_entries
Expand Down
4 changes: 4 additions & 0 deletions misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1422,6 +1422,10 @@ GC_API void GC_CALL GC_init(void)
# if defined(GWW_VDB) && !defined(KEEP_BACK_PTRS)
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

0 comments on commit a77a4f9

Please sign in to comment.