diff options
-rw-r--r-- | finalize.c | 423 | ||||
-rw-r--r-- | include/gc.h | 25 | ||||
-rw-r--r-- | tests/test.c | 49 |
3 files changed, 354 insertions, 143 deletions
@@ -33,23 +33,27 @@ struct hash_chain_entry { struct hash_chain_entry * next; }; -STATIC struct disappearing_link { +struct disappearing_link { struct hash_chain_entry prolog; # define dl_hidden_link prolog.hidden_key /* Field to be cleared. */ # define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) -# define dl_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y)) - +# define dl_set_next(x, y) \ + (void)((x)->prolog.next = (struct hash_chain_entry *)(y)) word dl_hidden_obj; /* Pointer to object base */ -} **GC_dl_head = 0; +}; -static signed_word log_dl_table_size = -1; - /* Binary log of current size of array pointed */ - /* to by GC_dl_head. If -1 then size is 0. */ +struct dl_hashtbl_s { + struct disappearing_link **head; + signed_word log_size; + word entries; +}; -STATIC word GC_dl_entries = 0; - /* Number of entries currently in disappearing */ - /* link table. */ +STATIC struct dl_hashtbl_s GC_dl_hashtbl = { + /* head */ NULL, /* log_size */ -1, /* entries */ 0 }; +#ifndef GC_LONG_REFS_NOT_NEEDED + STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 }; +#endif STATIC struct finalizable_object { struct hash_chain_entry prolog; @@ -72,11 +76,18 @@ static signed_word log_fo_table_size = -1; GC_INNER void GC_push_finalizer_structures(void) { - GC_ASSERT((word)&GC_dl_head % sizeof(word) == 0); + GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0); GC_ASSERT((word)&GC_fo_head % sizeof(word) == 0); GC_ASSERT((word)&GC_finalize_now % sizeof(word) == 0); - GC_push_all((ptr_t)(&GC_dl_head), (ptr_t)(&GC_dl_head) + sizeof(word)); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_ASSERT((word)&GC_ll_hashtbl.head % sizeof(word) == 0); + GC_push_all((ptr_t)(&GC_ll_hashtbl.head), + (ptr_t)(&GC_ll_hashtbl.head) + sizeof(word)); +# endif + + GC_push_all((ptr_t)(&GC_dl_hashtbl.head), + (ptr_t)(&GC_dl_hashtbl.head) + sizeof(word)); GC_push_all((ptr_t)(&GC_fo_head), (ptr_t)(&GC_fo_head) + sizeof(word)); GC_push_all((ptr_t)(&GC_finalize_now), (ptr_t)(&GC_finalize_now) + sizeof(word)); @@ -85,8 +96,7 @@ GC_INNER void GC_push_finalizer_structures(void) /* Double the size of a hash table. *size_ptr is the log of its current */ /* size. May be a no-op. */ /* *table is a pointer to an array of hash headers. If we succeed, we */ -/* update both *table and *log_size_ptr. */ -/* Lock is held. */ +/* update both *table and *log_size_ptr. Lock is held. */ STATIC void GC_grow_table(struct hash_chain_entry ***table, signed_word *log_size_ptr) { @@ -134,27 +144,26 @@ GC_API int GC_CALL GC_register_disappearing_link(void * * link) return(GC_general_register_disappearing_link(link, base)); } -GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, - const void * obj) +STATIC int GC_register_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link, + const void *obj) { struct disappearing_link *curr_dl; size_t index; struct disappearing_link * new_dl; DCL_LOCK_STATE; - if (((word)link & (ALIGNMENT-1)) || link == NULL) - ABORT("Bad arg to GC_general_register_disappearing_link"); LOCK(); GC_ASSERT(obj != NULL && GC_base_C(obj) == obj); - if (log_dl_table_size == -1 - || GC_dl_entries > ((word)1 << log_dl_table_size)) { - GC_grow_table((struct hash_chain_entry ***)&GC_dl_head, - &log_dl_table_size); + if (dl_hashtbl -> log_size == -1 + || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) { + GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head, + &dl_hashtbl -> log_size); GC_COND_LOG_PRINTF("Grew dl table to %u entries\n", - 1 << (unsigned)log_dl_table_size); + 1 << (unsigned)dl_hashtbl -> log_size); } - index = HASH2(link, log_dl_table_size); - for (curr_dl = GC_dl_head[index]; curr_dl != 0; + index = HASH2(link, dl_hashtbl -> log_size); + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); @@ -175,9 +184,9 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, /* It's not likely we'll make it here, but ... */ LOCK(); /* Recalculate index since the table may grow. */ - index = HASH2(link, log_dl_table_size); + index = HASH2(link, dl_hashtbl -> log_size); /* Check again that our disappearing link not in the table. */ - for (curr_dl = GC_dl_head[index]; curr_dl != 0; + for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); @@ -192,107 +201,178 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, } new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj); new_dl -> dl_hidden_link = GC_HIDE_POINTER(link); - dl_set_next(new_dl, GC_dl_head[index]); - GC_dl_head[index] = new_dl; - GC_dl_entries++; + dl_set_next(new_dl, dl_hashtbl -> head[index]); + dl_hashtbl -> head[index] = new_dl; + dl_hashtbl -> entries++; UNLOCK(); return GC_SUCCESS; } -GC_API int GC_CALL GC_unregister_disappearing_link(void * * link) +GC_API int GC_CALL GC_general_register_disappearing_link(void * * link, + const void * obj) { - struct disappearing_link *curr_dl, *prev_dl; - size_t index; - DCL_LOCK_STATE; + if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link) + ABORT("Bad arg to GC_general_register_disappearing_link"); + return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj); +} - if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */ +#ifdef DBG_HDRS_ALL +# define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL) +#else +# define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl) +#endif - LOCK(); - index = HASH2(link, log_dl_table_size); - prev_dl = 0; - for (curr_dl = GC_dl_head[index]; curr_dl != 0; +/* Unregisters given link and returns the link entry to free. */ +/* Assume the lock is held. */ +GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, void **link) +{ + struct disappearing_link *curr_dl; + struct disappearing_link *prev_dl = NULL; + size_t index = HASH2(link, dl_hashtbl->log_size); + + for (curr_dl = dl_hashtbl -> head[index]; curr_dl; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) { - if (prev_dl == 0) { - GC_dl_head[index] = dl_next(curr_dl); + /* Remove found entry from the table. */ + if (NULL == prev_dl) { + dl_hashtbl -> head[index] = dl_next(curr_dl); } else { dl_set_next(prev_dl, dl_next(curr_dl)); } - GC_dl_entries--; - UNLOCK(); -# ifdef DBG_HDRS_ALL - dl_set_next(curr_dl, 0); -# else - GC_free((void *)curr_dl); -# endif - return(1); + dl_hashtbl -> entries--; + break; } prev_dl = curr_dl; } + return curr_dl; +} + +GC_API int GC_CALL GC_unregister_disappearing_link(void * * link) +{ + struct disappearing_link *curr_dl; + DCL_LOCK_STATE; + + if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link); UNLOCK(); - return(0); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; } +#ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj) + { + if (((word)link & (ALIGNMENT-1)) != 0 || NULL == link) + ABORT("Bad arg to GC_register_long_link"); + return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj); + } + + GC_API int GC_CALL GC_unregister_long_link(void * * link) + { + struct disappearing_link *curr_dl; + DCL_LOCK_STATE; + + if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */ + + LOCK(); + curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link); + UNLOCK(); + if (NULL == curr_dl) return 0; + FREE_DL_ENTRY(curr_dl); + return 1; + } +#endif /* !GC_LONG_REFS_NOT_NEEDED */ + #ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED - GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link) + /* Moves a link. Assume the lock is held. */ + STATIC int GC_move_disappearing_link_inner( + struct dl_hashtbl_s *dl_hashtbl, + void **link, void **new_link) { struct disappearing_link *curr_dl, *prev_dl, *new_dl; size_t curr_index, new_index; word curr_hidden_link; word new_hidden_link; - DCL_LOCK_STATE; - if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL) - ABORT("Bad new_link arg to GC_move_disappearing_link"); - if (((word)link & (ALIGNMENT-1)) != 0) - return GC_NOT_FOUND; /* Nothing to do. */ - - LOCK(); /* Find current link. */ - curr_index = HASH2(link, log_dl_table_size); + curr_index = HASH2(link, dl_hashtbl -> log_size); curr_hidden_link = GC_HIDE_POINTER(link); - prev_dl = 0; - for (curr_dl = GC_dl_head[curr_index]; curr_dl != 0; + prev_dl = NULL; + for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == curr_hidden_link) break; prev_dl = curr_dl; } - if (curr_dl == 0) { - UNLOCK(); + if (NULL == curr_dl) { return GC_NOT_FOUND; } if (link == new_link) { - UNLOCK(); return GC_SUCCESS; /* Nothing to do. */ } /* link found; now check new_link not present. */ - new_index = HASH2(new_link, log_dl_table_size); + new_index = HASH2(new_link, dl_hashtbl -> log_size); new_hidden_link = GC_HIDE_POINTER(new_link); - for (new_dl = GC_dl_head[new_index]; new_dl != 0; + for (new_dl = dl_hashtbl -> head[new_index]; new_dl; new_dl = dl_next(new_dl)) { if (new_dl -> dl_hidden_link == new_hidden_link) { /* Target already registered; bail. */ - UNLOCK(); return GC_DUPLICATE; } } /* Remove from old, add to new, update link. */ - if (prev_dl == 0) { - GC_dl_head[curr_index] = dl_next(curr_dl); + if (NULL == prev_dl) { + dl_hashtbl -> head[curr_index] = dl_next(curr_dl); } else { dl_set_next(prev_dl, dl_next(curr_dl)); } curr_dl -> dl_hidden_link = new_hidden_link; - dl_set_next(curr_dl, GC_dl_head[new_index]); - GC_dl_head[new_index] = curr_dl; - UNLOCK(); + dl_set_next(curr_dl, dl_hashtbl -> head[new_index]); + dl_hashtbl -> head[new_index] = curr_dl; return GC_SUCCESS; } + + GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link) + { + int result; + DCL_LOCK_STATE; + + if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL) + ABORT("Bad new_link arg to GC_move_disappearing_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link); + UNLOCK(); + return result; + } + +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_API int GC_CALL GC_move_long_link(void **link, void **new_link) + { + int result; + DCL_LOCK_STATE; + + if (((word)new_link & (ALIGNMENT-1)) != 0 || new_link == NULL) + ABORT("Bad new_link arg to GC_move_disappearing_link"); + if (((word)link & (ALIGNMENT-1)) != 0) + return GC_NOT_FOUND; /* Nothing to do. */ + + LOCK(); + result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link); + UNLOCK(); + return result; + } +# endif /* !GC_LONG_REFS_NOT_NEEDED */ #endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */ /* Possible finalization_marker procedures. Note that mark stack */ @@ -510,24 +590,38 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, } #ifndef NO_DEBUGGING - void GC_dump_finalization(void) + STATIC void GC_dump_finalization_links( + const struct dl_hashtbl_s *dl_hashtbl) { - struct disappearing_link * curr_dl; - struct finalizable_object * curr_fo; + struct disappearing_link *curr_dl; ptr_t real_ptr, real_link; - int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); - int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); - int i; + size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : + 1 << dl_hashtbl->log_size; + size_t i; - GC_printf("Disappearing links:\n"); for (i = 0; i < dl_size; i++) { - for (curr_dl = GC_dl_head[i]; curr_dl != 0; + for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj); real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link); - GC_printf("Object: %p, Link:%p\n", real_ptr, real_link); + GC_printf("Object: %p, link: %p\n", real_ptr, real_link); } } + } + + void GC_dump_finalization(void) + { + struct finalizable_object * curr_fo; + size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size; + ptr_t real_ptr; + size_t i; + + GC_printf("Disappearing (short) links:\n"); + GC_dump_finalization_links(&GC_dl_hashtbl); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_printf("Disappearing long links:\n"); + GC_dump_finalization_links(&GC_ll_hashtbl); +# endif GC_printf("Finalizers:\n"); for (i = 0; i < fo_size; i++) { for (curr_fo = GC_fo_head[i]; curr_fo != 0; @@ -541,7 +635,10 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, #ifndef SMALL_CONFIG STATIC word GC_old_dl_entries = 0; /* for stats printing */ -#endif +# ifndef GC_LONG_REFS_NOT_NEEDED + STATIC word GC_old_ll_entries = 0; +# endif +#endif /* !SMALL_CONFIG */ #ifndef THREADS /* Global variables to minimize the level of recursion when a client */ @@ -571,47 +668,89 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, } #endif /* THREADS */ +#define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \ + { \ + size_t i; \ + size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \ + 1 << dl_hashtbl->log_size; \ + for (i = 0; i < dl_size; i++) { \ + curr_dl = dl_hashtbl -> head[i]; \ + prev_dl = NULL; \ + while (curr_dl) { + +#define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \ + prev_dl = curr_dl; \ + curr_dl = dl_next(curr_dl); \ + } \ + } \ + } + +#define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \ + { \ + next_dl = dl_next(curr_dl); \ + if (NULL == prev_dl) { \ + dl_hashtbl -> head[i] = next_dl; \ + } else { \ + dl_set_next(prev_dl, next_dl); \ + } \ + GC_clear_mark_bit(curr_dl); \ + dl_hashtbl -> entries--; \ + curr_dl = next_dl; \ + continue; \ + } + +GC_INLINE void GC_make_disappearing_links_disappear( + struct dl_hashtbl_s* dl_hashtbl) +{ + struct disappearing_link *curr, *prev, *next; + ptr_t real_ptr, real_link; + + ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev) + real_ptr = GC_REVEAL_POINTER(curr -> dl_hidden_obj); + real_link = GC_REVEAL_POINTER(curr -> dl_hidden_link); + if (!GC_is_marked(real_ptr)) { + *(word *)real_link = 0; + GC_clear_mark_bit(curr); + DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next); + } + ITERATE_DL_HASHTBL_END(curr, prev) +} + +GC_INLINE void GC_remove_dangling_disappearing_links( + struct dl_hashtbl_s* dl_hashtbl) +{ + struct disappearing_link *curr, *prev, *next; + ptr_t real_link; + + ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev) + real_link = GC_base(GC_REVEAL_POINTER(curr -> dl_hidden_link)); + if (NULL != real_link && !GC_is_marked(real_link)) { + GC_clear_mark_bit(curr); + DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next); + } + ITERATE_DL_HASHTBL_END(curr, prev) +} + /* Called with held lock (but the world is running). */ /* Cause disappearing links to disappear and unreachable objects to be */ /* enqueued for finalization. */ GC_INNER void GC_finalize(void) { - struct disappearing_link * curr_dl, * prev_dl, * next_dl; struct finalizable_object * curr_fo, * prev_fo, * next_fo; - ptr_t real_ptr, real_link; + ptr_t real_ptr; size_t i; - size_t dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); - size_t fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); + size_t fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size; # ifndef SMALL_CONFIG - /* Save current GC_dl_entries value for stats printing */ - GC_old_dl_entries = GC_dl_entries; + /* Save current GC_[dl/ll]_entries value for stats printing */ + GC_old_dl_entries = GC_dl_hashtbl.entries; +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_old_ll_entries = GC_ll_hashtbl.entries; +# endif # endif - /* Make disappearing links disappear */ - for (i = 0; i < dl_size; i++) { - curr_dl = GC_dl_head[i]; - prev_dl = 0; - while (curr_dl != 0) { - real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj); - real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link); - if (!GC_is_marked(real_ptr)) { - *(word *)real_link = 0; - next_dl = dl_next(curr_dl); - if (prev_dl == 0) { - GC_dl_head[i] = next_dl; - } else { - dl_set_next(prev_dl, next_dl); - } - GC_clear_mark_bit(curr_dl); - GC_dl_entries--; - curr_dl = next_dl; - } else { - prev_dl = curr_dl; - curr_dl = dl_next(curr_dl); - } - } - } + GC_make_disappearing_links_disappear(&GC_dl_hashtbl); + /* Mark all objects reachable via chains of 1 or more pointers */ /* from finalizable objects. */ GC_ASSERT(GC_mark_state == MS_NONE); @@ -719,28 +858,12 @@ GC_INNER void GC_finalize(void) } } - /* Remove dangling disappearing links. */ - for (i = 0; i < dl_size; i++) { - curr_dl = GC_dl_head[i]; - prev_dl = 0; - while (curr_dl != 0) { - real_link = GC_base(GC_REVEAL_POINTER(curr_dl -> dl_hidden_link)); - if (real_link != 0 && !GC_is_marked(real_link)) { - next_dl = dl_next(curr_dl); - if (prev_dl == 0) { - GC_dl_head[i] = next_dl; - } else { - dl_set_next(prev_dl, next_dl); - } - GC_clear_mark_bit(curr_dl); - GC_dl_entries--; - curr_dl = next_dl; - } else { - prev_dl = curr_dl; - curr_dl = dl_next(curr_dl); - } - } - } + GC_remove_dangling_disappearing_links(&GC_dl_hashtbl); +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_make_disappearing_links_disappear(&GC_ll_hashtbl); + GC_remove_dangling_disappearing_links(&GC_ll_hashtbl); +# endif + if (GC_fail_count) { /* Don't prevent running finalizers if there has been an allocation */ /* failure recently. */ @@ -762,7 +885,7 @@ GC_INNER void GC_finalize(void) register int i; int fo_size; - fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); + fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size; GC_bytes_finalized = 0; for (i = 0; i < fo_size; i++) { curr_fo = GC_fo_head[i]; @@ -959,18 +1082,32 @@ GC_INNER void GC_notify_or_invoke_finalizers(void) } #ifndef SMALL_CONFIG +# ifndef GC_LONG_REFS_NOT_NEEDED +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (x) +# else +# define IF_LONG_REFS_PRESENT_ELSE(x,y) (y) +# endif + GC_INNER void GC_print_finalization_stats(void) { - struct finalizable_object *fo = GC_finalize_now; + struct finalizable_object *fo; unsigned long ready = 0; - GC_stats_log_printf( - "%lu finalization table entries; %lu disappearing links alive\n", - (unsigned long)GC_fo_entries, (unsigned long)GC_dl_entries); - for (; 0 != fo; fo = fo_next(fo)) ++ready; - GC_stats_log_printf("%lu objects are eligible for immediate finalization;" - " %ld links cleared\n", - ready, (long)GC_old_dl_entries - (long)GC_dl_entries); + GC_stats_log_printf("%lu finalization entries;" + " %lu/%lu short/long disappearing links alive\n", + (unsigned long)GC_fo_entries, + (unsigned long)GC_dl_hashtbl.entries, + (unsigned long)IF_LONG_REFS_PRESENT_ELSE( + GC_ll_hashtbl.entries, 0)); + + for (fo = GC_finalize_now; 0 != fo; fo = fo_next(fo)) + ++ready; + GC_stats_log_printf("%lu finalization-ready objects;" + " %ld/%ld short/long links cleared\n", + ready, + (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, + (long)IF_LONG_REFS_PRESENT_ELSE( + GC_old_ll_entries - GC_ll_hashtbl.entries, 0)); } #endif /* !SMALL_CONFIG */ diff --git a/include/gc.h b/include/gc.h index 4063e794..7b9cbfe4 100644 --- a/include/gc.h +++ b/include/gc.h @@ -842,6 +842,8 @@ GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL # define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ GC_general_register_disappearing_link(link, \ GC_base((/* no const */ void *)(obj))) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_register_long_link(link, GC_base((/* no const */ void *)(obj))) # define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) #else # define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) @@ -867,6 +869,8 @@ GC_API /* 'realloc' attr */ GC_ATTR_ALLOC_SIZE(2) void * GC_CALL # define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) # define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_LONG_LINK(link, obj) \ + GC_register_long_link(link, obj) # define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) #endif /* !GC_DEBUG */ @@ -1092,6 +1096,27 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */); /* routines. Returns 0 if link was not actually */ /* registered (otherwise returns 1). */ +GC_API int GC_CALL GC_register_long_link(void ** /* link */, + const void * /* obj */) + GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2); + /* Similar to GC_general_register_disappearing_link but */ + /* *link only gets cleared when obj becomes truly */ + /* inaccessible. An object becomes truly inaccessible */ + /* when it can no longer be resurrected from its */ + /* finalizer (e.g. by assigning itself to a pointer */ + /* traceable from root). This can be used to implement */ + /* long weak pointers easily and safely. */ + +GC_API int GC_CALL GC_move_long_link(void ** /* link */, + void ** /* new_link */) + GC_ATTR_NONNULL(2); + /* Similar to GC_move_disappearing_link but for a link */ + /* previously registered via GC_register_long_link. */ + +GC_API int GC_CALL GC_unregister_long_link(void ** /* link */); + /* Similar to GC_unregister_disappearing_link but for a */ + /* registration by either of the above two routines. */ + /* Returns !=0 if GC_invoke_finalizers has something to do. */ GC_API int GC_CALL GC_should_invoke_finalizers(void); diff --git a/tests/test.c b/tests/test.c index 7309755b..15c0e110 100644 --- a/tests/test.c +++ b/tests/test.c @@ -742,9 +742,15 @@ size_t counter = 0; # if !defined(MACOS) GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0}; +# ifndef GC_LONG_REFS_NOT_NEEDED + GC_FAR void *live_long_refs[MAX_FINALIZED] = { NULL }; +# endif #else /* Too big for THINK_C. have to allocate it dynamically. */ GC_word *live_indicators = 0; +# ifndef GC_LONG_REFS_NOT_NEEDED +# define GC_LONG_REFS_NOT_NEEDED +# endif #endif int live_indicators_count = 0; @@ -845,6 +851,36 @@ tn * mktree(int n) GC_printf("GC_general_register_disappearing_link failed 2\n"); FAIL; } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) { + GC_printf("GC_register_long_link failed\n"); + FAIL; + } + if (GC_move_long_link(&live_long_refs[my_index], + &live_long_refs[my_index]) != GC_SUCCESS) { + GC_printf("GC_move_long_link(link,link) failed\n"); + FAIL; + } + new_link = live_long_refs[my_index]; + if (GC_move_long_link(&live_long_refs[my_index], + &new_link) != GC_SUCCESS) { + GC_printf("GC_move_long_link(new_link) failed\n"); + FAIL; + } + if (GC_unregister_long_link(&new_link) == 0) { + GC_printf("GC_unregister_long_link failed\n"); + FAIL; + } + if (GC_move_long_link(&live_long_refs[my_index], + &new_link) != GC_NOT_FOUND) { + GC_printf("GC_move_long_link(new_link) failed 2\n"); + FAIL; + } + if (GC_REGISTER_LONG_LINK(&live_long_refs[my_index], result) != 0) { + GC_printf("GC_register_long_link failed 2\n"); + FAIL; + } +# endif # endif GC_reachable_here(result); } @@ -1350,6 +1386,9 @@ void check_heap_stats(void) int i; # ifndef GC_NO_FINALIZATION int still_live; +# ifndef GC_LONG_REFS_NOT_NEEDED + int still_long_live = 0; +# endif # ifdef FINALIZE_ON_DEMAND int late_finalize_count = 0; # endif @@ -1438,6 +1477,11 @@ void check_heap_stats(void) if (live_indicators[i] != 0) { still_live++; } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (live_long_refs[i] != NULL) { + still_long_live++; + } +# endif } i = finalizable_count - finalized_count - still_live; if (0 != i) { @@ -1449,6 +1493,11 @@ void check_heap_stats(void) GC_printf("\tSlightly suspicious, but probably OK\n"); } } +# ifndef GC_LONG_REFS_NOT_NEEDED + if (0 != still_long_live) { + GC_printf("%d 'long' links remain\n", still_long_live); + } +# endif # endif GC_printf("Total number of bytes allocated is %lu\n", (unsigned long)GC_get_total_bytes()); |