summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--finalize.c423
-rw-r--r--include/gc.h25
-rw-r--r--tests/test.c49
3 files changed, 354 insertions, 143 deletions
diff --git a/finalize.c b/finalize.c
index 32c9394e..f50cb261 100644
--- a/finalize.c
+++ b/finalize.c
@@ -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());