diff --git a/fnlz_mlc.c b/fnlz_mlc.c index a56bbb847..0bb095640 100644 --- a/fnlz_mlc.c +++ b/fnlz_mlc.c @@ -130,4 +130,67 @@ GC_API GC_ATTR_MALLOC void * GC_CALL GC_finalized_malloc(size_t lb, return (word *)op + 1; } +#ifdef BUFFERED_FINALIZATION + +STATIC __thread void** finalizer_buffer_start; +STATIC __thread void** finalizer_buffer_next; +STATIC __thread void** finalizer_buffer_end; + +STATIC GC_push_to_fin_q_proc push_proc; + +void GC_new_buffer() { + GC_ASSERT(I_HOLD_LOCK()); + finalizer_buffer_start = (void**) GC_os_get_mem(GC_page_size); + finalizer_buffer_next = finalizer_buffer_start; + if (NULL == finalizer_buffer_next) + ABORT("Insufficient memory for finalization queue."); + + finalizer_buffer_end = (void*)finalizer_buffer_start + GC_page_size; +} + +STATIC int GC_CALLBACK GC_push_to_fin_q(void *obj) +{ + if (!GC_is_finalizer_queued_bit_set(obj)) + return 0; + + if (finalizer_buffer_next == finalizer_buffer_end) { + (push_proc)(finalizer_buffer_start); + GC_new_buffer(); + } + + *finalizer_buffer_next = obj; + finalizer_buffer_next++; + 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; + GC_set_finalizer_queued_bit(op); + return (word *)op; +} + + +GC_API void GC_CALL GC_init_buffered_finalization(GC_push_to_fin_q_proc p) +{ + GC_init(); /* In case it's not already done. */ + 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); + + push_proc = p; + GC_register_disclaim_proc_inner(GC_fin_q_kind, GC_push_to_fin_q, TRUE); + UNLOCK(); +} + +#endif /* BUFFERED_FINALIZATION */ + #endif /* ENABLE_DISCLAIM */ diff --git a/include/gc/gc_disclaim.h b/include/gc/gc_disclaim.h index aa3930ed0..454e61030 100644 --- a/include/gc/gc_disclaim.h +++ b/include/gc/gc_disclaim.h @@ -68,6 +68,39 @@ 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); + +#ifdef BUFFERED_FINALIZATION + +typedef int (GC_CALLBACK * GC_push_to_fin_q_proc)(void ** /* buffer */); + +/* 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(GC_push_to_fin_q_proc); + +/* 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); + +#endif /* BUFFERED_FINALIZATION */ + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 792330f72..2fcabefaf 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -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 diff --git a/mark.c b/mark.c index c961d4403..2c8ea99ff 100644 --- a/mark.c +++ b/mark.c @@ -183,9 +183,19 @@ GC_INNER void GC_clear_hdr_marks(hdr *hhdr) last_bit = FINAL_MARK_BIT((size_t)hhdr->hb_sz); # endif - BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); +# ifdef BUFFERED_FINALIZATION + unsigned i; + size_t sz = (size_t)hhdr->hb_sz; + unsigned n_marks = (unsigned)FINAL_MARK_BIT(sz); + + for (i = 0; i <= n_marks; i += (unsigned)MARK_BIT_OFFSET(sz)) { + hhdr -> hb_marks[i] &= ~1; + } set_mark_bit_from_hdr(hhdr, last_bit); hhdr -> hb_n_marks = 0; +#else + BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); +#endif } /* Set all mark bits in the header. Used for uncollectible blocks. */