Added long weakref support.
authorZach Saw <zach.saw@gmail.com>
Mon, 19 Nov 2012 01:34:10 +0000 (12:34 +1100)
committerZach Saw <zach.saw@gmail.com>
Mon, 19 Nov 2012 01:35:17 +0000 (12:35 +1100)
* include/gc.h: Added 3 GC_API functions for long weakref support:
GC_register_long_link, GC_unregister_long_link and GC_move_long_link.
* include/gc.h: Added corresponding GC_REGISTER_LONG_LINK macros.
* finalize.c: Add implementation of the above functions,
GC_ll_hashtbl for long links and GC_old_ll_entries.
* finalize.c (GC_push_finalizer_structures): GC_push_all long link
hash table entries as well.
* finalize.c (GC_dump_finalization): Refactored dump
disappearing link code out to GC_dump_finalization_links accepting
an argument of pointer to struct dl_hashtbl_s.
* finalize.c (GC_print_finalization_stats): Added long link stats
print out.
* finalize.c (GC_finalize): Added saving current GC_ll_hashtbl
entry count to GC_old_ll_entries,
GC_make_disappearing_links_disappear and
GC_remove_dangling_disappearing_links for long links.

finalize.c
include/gc.h

index 257fb61..6d5f26e 100644 (file)
@@ -48,7 +48,8 @@ STATIC struct dl_hashtbl_s {
     struct disappearing_link **head;
     signed_word log_size;
     word entries;
-} GC_dl_hashtbl = { /* head */ NULL, /* log_size */ -1, /* entries */ 0};
+} GC_dl_hashtbl = { /* head */ NULL, /* log_size */ -1, /* entries */ 0},
+  GC_ll_hashtbl = { /* head */ NULL, /* log_size */ -1, /* entries */ 0};
 
 STATIC struct finalizable_object {
     struct hash_chain_entry prolog;
@@ -72,11 +73,14 @@ static signed_word log_fo_table_size = -1;
 GC_INNER void GC_push_finalizer_structures(void)
 {
     GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0);
+    GC_ASSERT((word)&GC_ll_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_hashtbl.head),
                 (ptr_t)(&GC_dl_hashtbl.head) + sizeof(word));
+    GC_push_all((ptr_t)(&GC_ll_hashtbl.head),
+                (ptr_t)(&GC_ll_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));
@@ -206,6 +210,13 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
     return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj);
 }
 
+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);
+}
+
 #ifdef DBG_HDRS_ALL
 # define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL)
 #else
@@ -253,6 +264,21 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
     return 1;
 }
 
+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;
+}
+
 #ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
   /* Moves a link.  Assume the lock is held.    */
   GC_INLINE int GC_move_disappearing_link_inner(
@@ -321,6 +347,22 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
     UNLOCK();
     return result;
   }
+
+  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_MOVE_DISAPPEARING_LINK_NOT_NEEDED */
 
 /* Possible finalization_marker procedures.  Note that mark stack       */
@@ -538,25 +580,35 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
 }
 
 #ifndef NO_DEBUGGING
-  void GC_dump_finalization(void)
+  GC_INLINE void GC_dump_finalization_links(struct dl_hashtbl_s* dl_hashtbl)
   {
     struct disappearing_link * curr_dl;
-    struct finalizable_object * curr_fo;
     ptr_t real_ptr, real_link;
-    int dl_size = GC_dl_hashtbl.log_size == -1 ? 0 :
-                                1 << GC_dl_hashtbl.log_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_hashtbl.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);
       }
     }
+  }
+
+  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);
+    GC_printf("Disappearing long links:\n");
+    GC_dump_finalization_links(&GC_ll_hashtbl);
     GC_printf("Finalizers:\n");
     for (i = 0; i < fo_size; i++) {
       for (curr_fo = GC_fo_head[i]; curr_fo != 0;
@@ -569,7 +621,8 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
 #endif /* !NO_DEBUGGING */
 
 #ifndef SMALL_CONFIG
-  STATIC word GC_old_dl_entries = 0; /* for stats printing */
+  /* for stats printing */
+  STATIC word GC_old_dl_entries = 0, GC_old_ll_entries = 0;
 #endif
 
 #ifndef THREADS
@@ -674,8 +727,9 @@ GC_INNER void GC_finalize(void)
     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 */
+      /* Save current GC_[dl/ll]_entries value for stats printing */
       GC_old_dl_entries = GC_dl_hashtbl.entries;
+      GC_old_ll_entries = GC_ll_hashtbl.entries;
 #   endif
 
     GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
@@ -787,7 +841,10 @@ GC_INNER void GC_finalize(void)
       }
   }
 
+  GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
+
   GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
+  GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
 
   if (GC_fail_count) {
     /* Don't prevent running finalizers if there has been an allocation */
@@ -1013,13 +1070,17 @@ GC_INNER void GC_notify_or_invoke_finalizers(void)
     unsigned long ready = 0;
 
     GC_log_printf(
-        "%lu finalization table entries; %lu disappearing links alive\n",
-        (unsigned long)GC_fo_entries, (unsigned long)GC_dl_hashtbl.entries);
+        "%lu finalization table entries; "
+        "%lu short/%lu long disappearing links alive\n",
+        (unsigned long)GC_fo_entries,
+        (unsigned long)GC_dl_hashtbl.entries,
+        (unsigned long)GC_ll_hashtbl.entries);
     for (; 0 != fo; fo = fo_next(fo)) ++ready;
-    GC_log_printf("%lu objects are eligible for immediate finalization; "
-                  "%ld links cleared\n",
+    GC_log_printf("%lu objects are ready for finalization; "
+                  "%ld short/%ld long links cleared\n",
                   ready,
-                  (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries);
+                  (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries,
+                  (long)GC_old_ll_entries - (long)GC_ll_hashtbl.entries);
   }
 #endif /* !SMALL_CONFIG */
 
index 4f32b48..c6e7919 100644 (file)
@@ -825,6 +825,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)
@@ -850,6 +852,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 */
 
@@ -1055,6 +1059,17 @@ GC_API int GC_CALL GC_general_register_disappearing_link(void ** /* link */,
         /* GC_NO_MEMORY if registration failed for lack of      */
         /* memory (and GC_oom_fn did not handle the problem).   */
 
+GC_API int GC_CALL GC_register_long_link(void ** /* link */,
+                                    const void * /* obj */)
+                        GC_ATTR_NONNULL(1) GC_ATTR_NONNULL(2);
+        /* Similar to the above but *link only gets cleared     */
+        /* obj becomes truly inaccessible. An object becomes    */
+        /* truly inaccessible when it can no longer be          */
+        /* resurrected from a 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_disappearing_link(void ** /* link */,
                                              void ** /* new_link */)
                         GC_ATTR_NONNULL(2);
@@ -1070,11 +1085,20 @@ GC_API int GC_CALL GC_move_disappearing_link(void ** /* link */,
         /* returned if new_link is equal to link), GC_NOT_FOUND */
         /* if no link is registered at the original location.   */
 
+GC_API int GC_CALL GC_move_long_link(void ** /* link */,
+                                     void ** /* new_link */)
+                        GC_ATTR_NONNULL(2);
+        /* Similar to the above but for a link previously       */
+        /* registered via GC_register_long_link.                */
+
 GC_API int GC_CALL GC_unregister_disappearing_link(void ** /* link */);
         /* Undoes a registration by either of the above two     */
         /* routines.  Returns 0 if link was not actually        */
         /* registered (otherwise returns 1).                    */
 
+GC_API int GC_CALL GC_unregister_long_link(void ** /* link */);
+        /* Similar to the above but for the long_link routines. */
+
 /* Returns !=0 if GC_invoke_finalizers has something to do.     */
 GC_API int GC_CALL GC_should_invoke_finalizers(void);