]> granicus.if.org Git - gc/commitdiff
Add disclaim callbacks for efficient finalization.
authorPetter Urkedal <urkedal@nbi.dk>
Sun, 1 Jul 2007 11:48:00 +0000 (13:48 +0200)
committerIvan Maidanski <ivmai@mail.ru>
Fri, 30 Sep 2011 13:34:38 +0000 (17:34 +0400)
Importing gc-20070403-disclaim-1.0.patch.

* configure.ac: Add --disable-disclaim option and set ENABLE_DISCLAIM macro
and conditional accordingly.
* include/include.am, Makefile.am Add sources disclaim.c and
include/gc_disclaim.h.
* Makefile.direct: Ditto and define ENABLE_DISCLAIM.

* include/private/gc_priv.h (obj_kind): Add ok_mark_unconditionally,
ok_disclaim_proc and ok_disclaim_cd.
* misc.c (GC_new_kind_inner): Initialize them.
* include/private/gc_priv.h (hblkhdr): Add HAS_DISCLAIM and
MARK_UNCONDITIONALLY flags.
* allchblk.c (setup_header): Set HAS_DISCLAIM and MARK_UNCONDITIONALLY flags
as indicated by the object kind.

* reclaim.c: Main adjustments to support the disclaim callbacks.
* mark.c (GC_push_unconditionally, GC_push_next_marked_uncollecable):
Unconditionally mark from objects in blocks flagged MARK_UNCONDITIONALLY.
This preserves links reachable from the finalizer when using the the
finalized object kind.

* disclaim.c: Add file with support functions for disclaim callbacks and
implementation of the "finalized" object kind.
* include/gc_disclaim.h: Add file providing the corresponding API.
* include/private/thread_local_alloc.h (thread_local_freelists): Add
finalized_freelists.
* thread_local_alloc.c: Initialize them.

* tests/tests.am, tests/disclaim_test.c, tests/disclaim_bench.c: Add
disclaim-related tests.

16 files changed:
Makefile.am
Makefile.direct
allchblk.c
configure.ac
disclaim.c [new file with mode: 0644]
include/gc_disclaim.h [new file with mode: 0644]
include/include.am
include/private/gc_priv.h
include/private/thread_local_alloc.h
mark.c
misc.c
reclaim.c
tests/disclaim_bench.c [new file with mode: 0644]
tests/disclaim_test.c [new file with mode: 0644]
tests/tests.am
thread_local_alloc.c

index 940b8bce7ea14e07a55e326f5a9fd52acb549e5a..aac9d235287cf20a06b7f5bfe6cdf7c6be4da654 100644 (file)
@@ -72,6 +72,10 @@ if WIN32_THREADS
 libgc_la_SOURCES += win32_threads.c
 endif
 
+if ENABLE_DISCLAIM
+libgc_la_SOURCES += disclaim.c
+endif
+
 if USE_INTERNAL_LIBATOMIC_OPS
 nodist_libgc_la_SOURCES = libatomic_ops/src/atomic_ops.c
 endif
index 0cf9426c904749c654f1af5b5d7c6532ae1543fc..85a1e2397b8e778303e6bbb638e3ea61b9773071 100644 (file)
@@ -36,7 +36,7 @@ VPATH= $(srcdir)
 AO_SRC_DIR=$(srcdir)/libatomic_ops
 AO_INSTALL_DIR=$(srcdir)/libatomic_ops-install
 
-CFLAGS= -O -I$(srcdir)/include -I$(AO_INSTALL_DIR)/include -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS
+CFLAGS= -O -I$(srcdir)/include -I$(AO_INSTALL_DIR)/include -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DENABLE_DISCLAIM
 
 # To build the parallel collector on Linux, add to the above:
 # -DGC_LINUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC
@@ -74,14 +74,16 @@ OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o \
   headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o  \
   malloc.o stubborn.o checksums.o pthread_support.o pthread_stop_world.o \
   darwin_stop_world.o typd_mlc.o ptr_chck.o mallocx.o gcj_mlc.o specific.o \
-  gc_dlopen.o backgraph.o win32_threads.o pthread_start.o thread_local_alloc.o
+  gc_dlopen.o backgraph.o win32_threads.o pthread_start.o thread_local_alloc.o \
+  disclaim.o
 
 CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c \
   headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c \
   new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c \
   checksums.c pthread_support.c pthread_stop_world.c darwin_stop_world.c \
   typd_mlc.c ptr_chck.c mallocx.c gcj_mlc.c specific.c gc_dlopen.c \
-  backgraph.c win32_threads.c pthread_start.c thread_local_alloc.c
+  backgraph.c win32_threads.c pthread_start.c thread_local_alloc.c \
+  disclaim.c
 
 CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/tests/de.c \
   cord/tests/cordtest.c include/cord.h include/ec.h \
@@ -101,7 +103,7 @@ SRCS= $(CSRCS) \
   include/javaxfc.h \
   include/gc_backptr.h include/gc_gcj.h include/private/dbg_mlc.h \
   include/private/specific.h include/leak_detector.h \
-  include/gc_pthread_redirects.h \
+  include/gc_pthread_redirects.h include/gc_disclaim.h \
   include/gc_config_macros.h include/private/pthread_support.h \
   include/private/pthread_stop_world.h include/private/darwin_semaphore.h \
   include/private/darwin_stop_world.h include/private/thread_local_alloc.h \
index 77e036a95c0f3faa878adc6d01936f3fae39ec50..fbe6dd7e1521f1c14895c4f3439d83466b01106e 100644 (file)
@@ -225,6 +225,12 @@ static GC_bool setup_header(hdr * hhdr, struct hblk *block, size_t byte_sz,
 #   ifndef MARK_BIT_PER_OBJ
       size_t granules;
 #   endif
+#   ifdef ENABLE_DISCLAIM
+      if (GC_obj_kinds[kind].ok_disclaim_proc)
+        flags |= HAS_DISCLAIM;
+      if (GC_obj_kinds[kind].ok_mark_unconditionally)
+        flags |= MARK_UNCONDITIONALLY;
+#   endif
 
     /* Set size, kind and mark proc fields */
       hhdr -> hb_sz = byte_sz;
index 6cd63ae0c9a5d0796e9d9e95d48e820095ccb9d6..6c70cd2a5ef3c452d5e49269c7ddbff1bd32130d 100644 (file)
@@ -696,6 +696,16 @@ if test "${enable_redirect_malloc}" = yes; then
     AC_DEFINE([GC_USE_DLOPEN_WRAP], 1, [See doc/README.macros.])
 fi
 
+AC_ARG_ENABLE(disclaim,
+    [AC_HELP_STRING([--disable-disclaim],
+       [Disable alternative (more efficient) finalization interface.])])
+if test x"$enable_disclaim" != xno; then
+    AC_DEFINE(ENABLE_DISCLAIM, 1,
+       [Define to enable alternative finalization interface.])
+fi
+AM_CONDITIONAL(ENABLE_DISCLAIM,
+    [test x"$enable_disclaim" != xno])
+
 AC_ARG_ENABLE(large-config,
     [AC_HELP_STRING([--enable-large-config],
         [Optimize for large (> 100 MB) heap or root set])])
diff --git a/disclaim.c b/disclaim.c
new file mode 100644 (file)
index 0000000..e61ddb0
--- /dev/null
@@ -0,0 +1,144 @@
+#ifdef ENABLE_DISCLAIM
+
+#include "gc_disclaim.h"
+#include "private/gc_priv.h"
+#include "private/thread_local_alloc.h"
+
+
+/* Low level interface for reclaim callbacks. */
+
+int GC_register_disclaim_proc(int kind,
+                              int (*proc)(void *obj, void *cd), void *cd,
+                              int mark_unconditionally)
+{
+    GC_obj_kinds[kind].ok_disclaim_proc = proc;
+    GC_obj_kinds[kind].ok_disclaim_cd = cd;
+    GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally;
+}
+
+
+/* High level interface for finalization. */
+
+int GC_finalized_kind;
+
+int GC_finalized_debug_kind;
+
+ptr_t * GC_finalized_objfreelist;
+ptr_t * GC_finalized_debugobjfreelist;
+
+static int GC_finalized_disclaim(void *obj, void *cd)
+{
+    struct GC_finalizer_closure *fc = *(void **)obj;
+    if ((word)fc & 1) { /* See [1] */
+        fc = (void *)((word)fc & ~(word)1);
+        (*fc->proc)((void **)obj + 1, fc->cd);
+    }
+    return 0;
+}
+
+static int done_init = 0;
+void GC_init_finalized_malloc()
+{
+    DCL_LOCK_STATE;
+    if (done_init)
+        return;
+    GC_init();
+    LOCK();
+    if (done_init)
+        goto done;
+    done_init = 1;
+
+    GC_finalized_objfreelist = (ptr_t *)GC_new_free_list_inner();
+    GC_finalized_kind =
+        GC_new_kind_inner((void **)GC_finalized_objfreelist,
+                          0 | GC_DS_LENGTH,
+                          TRUE, TRUE);
+    GC_register_disclaim_proc(GC_finalized_kind, GC_finalized_disclaim, 0, 1);
+
+done:
+    UNLOCK();
+}
+
+ptr_t GC_clear_stack();
+
+#ifdef THREAD_LOCAL_ALLOC
+  void * GC_core_finalized_malloc(size_t lb, struct GC_finalizer_closure *fclos)
+#else
+  void * GC_finalized_malloc(size_t lb, struct GC_finalizer_closure *fclos)
+#endif
+{
+    register ptr_t op;
+    register ptr_t *opp;
+    DCL_LOCK_STATE;
+
+    lb += sizeof(void *);
+    if (!done_init)
+        ABORT("You must call GC_init_finalize_malloc before using "
+              "GC_malloc_with_finalizer.");
+    if (EXPECT(SMALL_OBJ(lb), 1)) {
+        register word lg;
+        lg = GC_size_map[lb];
+        opp = &GC_finalized_objfreelist[lg];
+        LOCK();
+        if (EXPECT((op = *opp) == 0, 0)) {
+            UNLOCK();
+            op = GC_generic_malloc((word)lb, GC_finalized_kind);
+        } else {
+            *opp = obj_link(op);
+            obj_link(op) = 0;
+            GC_bytes_allocd += GRANULES_TO_BYTES(lg);
+            UNLOCK();
+        }
+    } else
+        op = GC_generic_malloc((word)lb, GC_finalized_kind);
+    *(void **)op = (ptr_t)fclos + 1; /* See [1] */
+    return GC_clear_stack(op + sizeof(void *));
+}
+
+#ifdef THREAD_LOCAL_ALLOC
+void * GC_finalized_malloc(size_t client_lb, struct GC_finalizer_closure *fclos)
+{
+    size_t lb = client_lb + sizeof(void *);
+    size_t lg = ROUNDED_UP_GRANULES(lb);
+    GC_tlfs tsd;
+    void *result;
+    void **tiny_fl, **my_fl, *my_entry;
+    void *next;
+    if (GC_EXPECT(lg >= GC_TINY_FREELISTS, 0))
+        return GC_core_finalized_malloc(client_lb, fclos);
+
+    tsd = GC_getspecific(GC_thread_key);
+    tiny_fl = tsd->finalized_freelists;
+    my_fl = tiny_fl + lg;
+    my_entry = *my_fl;
+    while (GC_EXPECT((word)my_entry
+                     <= DIRECT_GRANULES + GC_TINY_FREELISTS + 1, 0)) {
+        if ((word)my_entry - 1 < DIRECT_GRANULES) {
+            *my_fl = (ptr_t)my_entry + lg + 1;
+            return GC_core_finalized_malloc(client_lb, fclos);
+        } else {
+            GC_generic_malloc_many(RAW_BYTES_FROM_INDEX(lg),
+                                   GC_finalized_kind, my_fl);
+            my_entry = *my_fl;
+            if (my_entry == 0)
+                return GC_oom_fn(lb);
+        }
+    }
+    next = obj_link(my_entry);
+    result = (void *)my_entry;
+    *my_fl = next;
+    *(void **)result = (ptr_t)fclos + 1;
+    PREFETCH_FOR_WRITE(next);
+    return (void **)result + 1;
+}
+#endif
+
+#endif /* ENABLE_DISCLAIM */
+
+/* [1] The disclaim function may be passed fragments from the free-list, on
+ * which it should not run finalization.  To recognize this case, we use
+ * the fact that the first word on such fragments are always even (a link
+ * to the next fragment, or NULL).  If it is desirable to have a finalizer
+ * which does not use the first word for storing finalization info,
+ * GC_reclaim_with_finalization must be extended to clear fragments so
+ * that the assumption holds for the selected word. */
diff --git a/include/gc_disclaim.h b/include/gc_disclaim.h
new file mode 100644 (file)
index 0000000..c67a9ce
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007 by Hewlett-Packard Company. All rights reserved.
+ *
+ * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
+ * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
+ *
+ * Permission is hereby granted to use or copy this program
+ * for any purpose,  provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ *
+ */
+
+#ifndef _GC_DISCLAIM_H
+#define _GC_DISCLAIM_H
+
+#include "gc.h"
+
+/* Register a function notifier_proc which will be called on each      */
+/* object of the given kind before it is reclaimed.  If notifier_proc  */
+/* returns non-zero, the collector will not reclaim the object on this */
+/* GC cycle.  Objects reachable from proc will be protected from       */
+/* collection if mark_from_all=1, but at the expense that long chains  */
+/* of objects will take many cycles to reclaim.                                */
+/* Not available if configured with --disable-disclaim.                        */
+int GC_register_disclaim_proc(int kind,
+                             int (*proc)(void *obj, void *cd), void *cd,
+                             int mark_from_all);
+
+/* The finalizer closure used by GC_finalized_malloc.                  */
+struct GC_finalizer_closure {
+    void (*proc)(void *obj, void *cd);
+    void *cd;
+};
+
+/* Allocate size bytes which is finalized with fc.  This uses a        */
+/* dedicated object kind with the disclaim mechanism for maximum.  It  */
+/* is more efficient than GC_register_finalizer and friends.  You need */
+/* to call GC_init_finalized_malloc before using this.                 */
+GC_API void *GC_finalized_malloc(size_t size, struct GC_finalizer_closure *fc);
+
+/* Prepare the object kind used for GC_finalized_malloc.               */
+GC_API void GC_init_finalized_malloc(void);
+
+#endif
index a793a6f44d87b936ca30f4beb472f3760fd23de5..a3c64d5858a2da2bffb3c54f977036c25fa6d4a7 100644 (file)
@@ -29,6 +29,11 @@ pkginclude_HEADERS += \
         include/gc_tiny_fl.h \
         include/gc_version.h
 
+if ENABLE_DISCLAIM
+pkginclude_HEADERS += include/gc_disclaim.h
+endif
+EXTRA_DIST += include/gc_disclaim.h
+
 # headers which are not installed
 #
 dist_noinst_HEADERS += \
index 8e78cb8b4d98b4b515afaf34557b34df27a83f51..77020f16e3c62a20d3ca59fbbfbace2df25f37e3 100644 (file)
@@ -814,6 +814,14 @@ struct hblkhdr {
                                 /* before it can be reallocated.        */
                                 /* Only set with USE_MUNMAP.            */
 #       define FREE_BLK 4       /* Block is free, i.e. not in use.      */
+#       ifdef ENABLE_DISCLAIM
+#           define HAS_DISCLAIM 8
+                                    /* This kind has a callback on reclaim. */
+#           define MARK_UNCONDITIONALLY 16
+                                /* Mark from all objects, marked or     */
+                                /* not. Used to mark objects needed by  */
+                                /* reclaim notifier.                    */
+#       endif
     unsigned short hb_last_reclaimed;
                                 /* Value of GC_gc_no when block was     */
                                 /* last allocated or swept. May wrap.   */
@@ -1224,6 +1232,18 @@ GC_EXTERN struct obj_kind {
                         /* template to obtain descriptor.  Otherwise    */
                         /* template is used as is.                      */
    GC_bool ok_init;   /* Clear objects before putting them on the free list. */
+#  ifdef ENABLE_DISCLAIM
+     GC_bool ok_mark_unconditionally;
+                        /* Mark from all, including unmarked, objects   */
+                        /* in block.  Used to protect objects reachable */
+                        /* from reclaim notifiers.                      */
+     int (*ok_disclaim_proc)(void *obj, void *cd);
+     void *ok_disclaim_cd;
+                        /* The disclaim procedure is called before obj  */
+                        /* is reclaimed, but must also tolerate being   */
+                        /* called with object from freelist.  Non-zero  */
+                        /* exit prevents object from being reclaimed.   */
+#  endif
 } GC_obj_kinds[MAXOBJKINDS];
 
 #define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds))
index 68d6d88bfa830ce9a7f6671918d7871ba525cfa6..ad790a2821672f37285460fe5f10aeb720e9fe93 100644 (file)
@@ -88,6 +88,9 @@ typedef struct thread_local_freelists {
 # define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES)
         /* Don't use local free lists for up to this much       */
         /* allocation.                                          */
+# ifdef ENABLE_DISCLAIM
+    void * finalized_freelists[TINY_FREELISTS];
+# endif
 } *GC_tlfs;
 
 #if defined(USE_PTHREAD_SPECIFIC)
diff --git a/mark.c b/mark.c
index f7b7437e721050ba1e8ff715ccc84d2e244feecc..a8773610f5b4217b08dac85b708b0002c4a1ffe6 100644 (file)
--- a/mark.c
+++ b/mark.c
@@ -1759,6 +1759,36 @@ STATIC void GC_push_marked(struct hblk *h, hdr *hhdr)
     }
 }
 
+#ifdef MARK_UNCONDITIONALLY
+/* Mark all objects which have not been reclaimed according to the convension
+ * that the first word is odd for live objects.  This is used optionally to
+ * guard the contents of objects passed to reclaim notifiers. */
+void GC_push_unconditionally(struct hblk *h, hdr *hhdr)
+{
+    int sz = hhdr -> hb_sz;
+    int descr = hhdr -> hb_descr;
+    ptr_t p;
+    ptr_t lim;
+    mse * GC_mark_stack_top_reg;
+    mse * mark_stack_limit = GC_mark_stack_limit;
+
+    /* Shortcut */
+        if ((0 | GC_DS_LENGTH) == descr) return;
+    GC_n_rescuing_pages++;
+    GC_objects_are_marked = TRUE;
+    if (sz > MAXOBJBYTES)
+        lim = h -> hb_body;
+    else
+        lim = (h + 1)->hb_body - sz;
+
+    GC_mark_stack_top_reg = GC_mark_stack_top;
+    for (p = h -> hb_body; p <= lim; p += sz)
+        if ((*(GC_word *)p & 0x3) != 0)
+            PUSH_OBJ(p, hhdr, GC_mark_stack_top_reg, mark_stack_limit);
+    GC_mark_stack_top = GC_mark_stack_top_reg;
+}
+#endif
+
 #ifndef GC_DISABLE_INCREMENTAL
   /* Test whether any page in the given block is dirty.   */
   STATIC GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr)
@@ -1839,10 +1869,18 @@ STATIC struct hblk * GC_push_next_marked_uncollectable(struct hblk *h)
           if (h == 0) return(0);
           hhdr = GC_find_header((ptr_t)h);
         }
-        if (hhdr -> hb_obj_kind == UNCOLLECTABLE) break;
+        if (hhdr -> hb_obj_kind == UNCOLLECTABLE) {
+            GC_push_marked(h, hhdr);
+            break;
+        }
+#       ifdef MARK_UNCONDITIONALLY
+            if (hhdr -> hb_flags & MARK_UNCONDITIONALLY) {
+                GC_push_unconditionally(h, hhdr);
+                break;
+            }
+#       endif
         h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
         hhdr = HDR(h);
     }
-    GC_push_marked(h, hhdr);
     return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz));
 }
diff --git a/misc.c b/misc.c
index 21554afe4a973e68fb4ada9de243e8b02c761a99..25a5cde38288c7da19c626ae3345245ddf5729b1 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -1474,6 +1474,10 @@ GC_API unsigned GC_CALL GC_new_kind_inner(void **fl, GC_word descr,
     GC_obj_kinds[result].ok_descriptor = descr;
     GC_obj_kinds[result].ok_relocate_descr = adjust;
     GC_obj_kinds[result].ok_init = clear;
+#   ifdef ENABLE_DISCLAIM
+       GC_obj_kinds[result].ok_disclaim_proc = 0;
+       GC_obj_kinds[result].ok_disclaim_cd = 0;
+#   endif
     return result;
 }
 
index 3d4a81d1be3d152be2ecd09fe8d2342251036dc7..f4be205d55d4be5915c8f3b91c8fb80ee8ef7529 100644 (file)
--- a/reclaim.c
+++ b/reclaim.c
@@ -15,6 +15,7 @@
  */
 
 #include "private/gc_priv.h"
+#include "gc_disclaim.h"
 
 #include <stdio.h>
 
@@ -117,6 +118,11 @@ GC_INNER void GC_print_all_errors(void)
 /* objects.  This does not require the block to be in physical memory.  */
 GC_INNER GC_bool GC_block_empty(hdr *hhdr)
 {
+    /* XXX: Only if reclaim notifiers have not been run. */
+#   ifdef ENABLE_DISCLAIM
+        if (hhdr -> hb_flags & HAS_DISCLAIM)
+            return FALSE;
+#   endif
     return (hhdr -> hb_n_marks == 0);
 }
 
@@ -206,6 +212,56 @@ STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, size_t sz,
     return(list);
 }
 
+#ifdef ENABLE_DISCLAIM
+/* Call reclaim notifier for block's kind on each unmarked object in    */
+/* block, all within a pair of corresponding enter/leave callbacks.     */
+ptr_t GC_disclaim_and_reclaim(struct hblk *hbp, hdr *hhdr, size_t sz,
+                              ptr_t list, signed_word *count)
+{
+    register int bit_no = 0;
+    register word *p, *q, *plim;
+    signed_word n_bytes_found = 0;
+    struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind];
+    int (*proc)(void *, void *) = ok -> ok_disclaim_proc;
+    void *cd = ok -> ok_disclaim_cd;
+
+    GC_ASSERT(sz == hhdr -> hb_sz);
+    p = (word *)(hbp -> hb_body);
+    plim = (word *)((ptr_t)p + HBLKSIZE - sz);
+
+    while (p <= plim) {
+        if (mark_bit_from_hdr(hhdr, bit_no) || (*proc)(p, cd))
+            p = (word *)((ptr_t)p + sz);
+        else {
+                n_bytes_found += sz;
+                /* object is available - put on list */
+                    obj_link(p) = list;
+                    list = ((ptr_t)p);
+                /* Clear object, advance p to next object in the process */
+                    q = (word *)((ptr_t)p + sz);
+#                   ifdef USE_MARK_BYTES
+                      GC_ASSERT(!(sz & 1)
+                                && !((word)p & (2 * sizeof(word) - 1)));
+                      p[1] = 0;
+                      p += 2;
+                      while (p < q) {
+                        CLEAR_DOUBLE(p);
+                        p += 2;
+                      }
+#                   else
+                      p++; /* Skip link field */
+                      while (p < q) {
+                        *p++ = 0;
+                      }
+#                   endif
+        }
+        bit_no += MARK_BIT_OFFSET(sz);
+    }
+    *count += n_bytes_found;
+    return list;
+}
+#endif /* ENABLE_DISCLAIM */
+
 /* Don't really reclaim objects, just check for unmarked ones: */
 STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz)
 {
@@ -237,6 +293,11 @@ GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz,
     GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr);
 #   ifndef GC_DISABLE_INCREMENTAL
       GC_remove_protection(hbp, 1, (hhdr)->hb_descr == 0 /* Pointer-free? */);
+#   endif
+#   ifdef ENABLE_DISCLAIM
+    if (hhdr -> hb_flags & HAS_DISCLAIM)
+      result = GC_disclaim_and_reclaim(hbp, hhdr, sz, list, count);
+    else
 #   endif
     if (init || GC_debugging_started) {
       result = GC_reclaim_clear(hbp, hhdr, sz, list, count);
@@ -293,6 +354,17 @@ STATIC void GC_reclaim_block(struct hblk *hbp, word report_if_found)
               GC_add_leaked((ptr_t)hbp);
             } else {
               size_t blocks = OBJ_SZ_TO_BLOCKS(sz);
+#             ifdef ENABLE_DISCLAIM
+                if (EXPECT(hhdr->hb_flags & HAS_DISCLAIM, 0)) {
+                  struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind];
+                  int resurrect;
+                  resurrect = (*ok->ok_disclaim_proc)(hbp, ok->ok_disclaim_cd);
+                  if (resurrect) {
+                    set_mark_bit_from_hdr(hhdr, 0);
+                    /* excuse me, */ goto in_use;
+                  }
+                }
+#             endif
               if (blocks > 1) {
                 GC_large_allocd_bytes -= blocks * HBLKSIZE;
               }
@@ -300,6 +372,7 @@ STATIC void GC_reclaim_block(struct hblk *hbp, word report_if_found)
               GC_freehblk(hbp);
             }
         } else {
+          in_use:
             if (hhdr -> hb_descr != 0) {
               GC_composite_in_use += sz;
             } else {
diff --git a/tests/disclaim_bench.c b/tests/disclaim_bench.c
new file mode 100644 (file)
index 0000000..af2375e
--- /dev/null
@@ -0,0 +1,115 @@
+#include "gc_disclaim.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static int nf = 0;
+
+typedef struct testobj_s *testobj_t;
+struct testobj_s {
+    testobj_t keep_link;
+    int i;
+};
+
+void testobj_finalize(void *obj, void *carg)
+{
+#define obj ((testobj_t)obj)
+    ++*(int *)carg;
+    assert(obj->i++ == 109);
+#undef obj
+}
+static struct GC_finalizer_closure fclos = {
+    testobj_finalize,
+    &nf
+};
+
+testobj_t testobj_new(int model)
+{
+    testobj_t obj;
+    switch (model) {
+       case 0:
+           obj = GC_malloc(sizeof(struct testobj_s));
+           GC_register_finalizer_no_order(obj, testobj_finalize, &nf,
+                                          NULL, NULL);
+           break;
+       case 1:
+           obj = GC_finalized_malloc(sizeof(struct testobj_s), &fclos);
+           break;
+       case 2:
+           obj = GC_malloc(sizeof(struct testobj_s));
+           break;
+       default:
+           abort();
+    }
+    obj->i = 109;
+    obj->keep_link = NULL;
+    return obj;
+}
+
+
+#define ALLOC_CNT (4*1024*1024)
+#define KEEP_CNT (32*1024)
+
+int main(int argc, char **argv)
+{
+    int i;
+    int repeat;
+    int model;
+    testobj_t *keep_arr;
+    double t;
+    static char const *model_str[3] = {
+       "regular finalization",
+       "finalize on reclaim",
+       "no finalization"
+    };
+
+    GC_init();
+    GC_init_finalized_malloc();
+
+    /* Seed with time for distict usage patters over repeated runs. */
+    srand48(time(NULL));
+
+    keep_arr = GC_malloc(sizeof(void *)*KEEP_CNT);
+
+    if (argc == 1) {
+       char *buf = GC_malloc(strlen(argv[0]) + 3);
+       printf("\t\t\tfin. ratio       time/s    time/fin.\n");
+       for (i = 0; i < 3; ++i) {
+           int st;
+           sprintf(buf, "%s %d", argv[0], i);
+           st = system(buf);
+           if (st != 0)
+               return st;
+       }
+       return 0;
+    }
+    if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+       fprintf(stderr,
+               "Usage: %s FINALIZATION_MODEL\n"
+               "\t0 -- original finalization\n"
+               "\t1 -- finalization on reclaim\n"
+               "\t2 -- no finalization\n", argv[0]);
+       return 1;
+    }
+    model = atoi(argv[1]);
+    if (model < 0 || model > 2)
+       exit(2);
+    t = -clock();
+    for (i = 0; i < ALLOC_CNT; ++i) {
+       int k = lrand48() % KEEP_CNT;
+       keep_arr[k] = testobj_new(model);
+    }
+    GC_gcollect();
+    t += clock();
+    t /= CLOCKS_PER_SEC;
+    if (model < 2)
+       printf("%20s: %12.4lf %12lg %12lg\n", model_str[model],
+              nf/(double)ALLOC_CNT, t, t/nf);
+    else
+       printf("%20s:            0 %12lg          N/A\n",
+              model_str[model], t);
+
+    return 0;
+}
diff --git a/tests/disclaim_test.c b/tests/disclaim_test.c
new file mode 100644 (file)
index 0000000..bc8462a
--- /dev/null
@@ -0,0 +1,138 @@
+/* Test that objects reachable from an object allocated with
+ * GC_malloc_with_finalizer is not reclaimable before the finalizer
+ * is called. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <gc_disclaim.h>
+
+typedef struct pair_s *pair_t;
+
+struct pair_s {
+    int is_valid;
+    int checksum;
+    pair_t car;
+    pair_t cdr;
+};
+
+void
+pair_dct(void *obj, void *cd)
+{
+    pair_t p = obj;
+    int checksum;
+
+    /* Check that obj and its car and cdr are not trashed. */
+    //printf("Destruct %p = (%p, %p)\n", p, p->car, p->cdr);
+    assert(GC_base(obj));
+    assert(p->is_valid);
+    assert(!p->car || p->car->is_valid);
+    assert(!p->cdr || p->cdr->is_valid);
+    checksum = 782;
+    if (p->car) checksum += p->car->checksum;
+    if (p->cdr) checksum += p->cdr->checksum;
+    assert(p->checksum == checksum);
+
+    /* Invalidate it. */
+    p->is_valid = 0;
+    p->checksum = 0;
+    p->car = NULL;
+    p->cdr = NULL;
+}
+
+pair_t
+pair_new(pair_t car, pair_t cdr)
+{
+    pair_t p;
+    static struct GC_finalizer_closure fc = { pair_dct, NULL };
+    p = GC_finalized_malloc(sizeof(struct pair_s), &fc);
+    p->is_valid = 1;
+    p->checksum = 782 + (car? car->checksum : 0) + (cdr? cdr->checksum : 0);
+    p->car = car;
+    p->cdr = cdr;
+    //printf("Construct %p = (%p, %p)\n", p, p->car, p->cdr);
+    return p;
+}
+
+void
+pair_check_rec(pair_t p)
+{
+    while (p) {
+       int checksum = 782;
+       if (p->car) checksum += p->car->checksum;
+       if (p->cdr) checksum += p->cdr->checksum;
+       assert(p->checksum == checksum);
+       if (rand() % 2)
+           p = p->car;
+       else
+           p = p->cdr;
+    }
+}
+
+#ifdef GC_PTHREADS
+#  define THREAD_CNT 6
+#else
+#  define THREAD_CNT 1
+#endif
+
+#define POP_SIZE 1000
+#if THREAD_CNT > 1
+#  define MUTATE_CNT 2000000/THREAD_CNT
+#else
+#  define MUTATE_CNT 10000000
+#endif
+#define GROW_LIMIT 10000000
+
+void *test(void *data)
+{
+    int i;
+    pair_t pop[POP_SIZE];
+    memset(pop, 0, sizeof(pop));
+    for (i = 0; i < MUTATE_CNT; ++i) {
+       int t = rand() % POP_SIZE;
+       switch (rand() % (i > GROW_LIMIT? 5 : 3)) {
+       case 0: case 3:
+           if (pop[t])
+               pop[t] = pop[t]->car;
+           break;
+       case 1: case 4:
+           if (pop[t])
+               pop[t] = pop[t]->cdr;
+           break;
+       case 2:
+           pop[t] = pair_new(pop[rand() % POP_SIZE],
+                             pop[rand() % POP_SIZE]);
+           break;
+       }
+       if (rand() % 8 == 1)
+           pair_check_rec(pop[rand() % POP_SIZE]);
+    }
+    return 0;
+}
+
+int main()
+{
+    pthread_t th[THREAD_CNT];
+    int i;
+    GC_init();
+    GC_init_finalized_malloc();
+#if THREAD_CNT > 1
+    printf("Threaded disclaim test.\n");
+    for (i = 0; i < THREAD_CNT; ++i) {
+       int err = pthread_create(&th[i], NULL, test, NULL);
+       if (err) {
+           fprintf(stderr, "Failed to create thread # %d: %s\n", i,
+                   strerror(err));
+           exit(1);
+       }
+    }
+    for (i = 0; i < THREAD_CNT; ++i)
+       pthread_join(th[i], NULL);
+#else
+    printf("Unthreaded disclaim test.\n");
+    test(NULL);
+#endif
+    return 0;
+}
+
index 13647b3a93c40592bedd6b8778636be612856d5c..f7985c9a6aea0dc94b133ae8ced07bf671c18d81 100644 (file)
@@ -91,3 +91,15 @@ else
 test_cpp_LDADD = libgccpp.la $(test_ldadd)
 endif
 endif
+
+if ENABLE_DISCLAIM
+TESTS += disclaim_test
+check_PROGRAMS += disclaim_test
+disclaim_test_SOURCES = tests/disclaim_test.c
+disclaim_test_LDADD = $(test_ldadd)
+
+TESTS += disclaim_bench
+check_PROGRAMS += disclaim_bench
+disclaim_bench_SOURCES = tests/disclaim_bench.c
+disclaim_bench_LDADD = $(test_ldadd)
+endif
index 610db6fd45caa08ca5ec401857a163f0824e3e04..ac35173463b35996767e76113eca0dda3d1efdda 100644 (file)
@@ -97,6 +97,9 @@ GC_INNER void GC_init_thread_local(GC_tlfs p)
         p -> normal_freelists[i] = (void *)(word)1;
 #       ifdef GC_GCJ_SUPPORT
           p -> gcj_freelists[i] = (void *)(word)1;
+#       endif
+#       ifdef ENABLE_DISCLAIM
+          p -> finalized_freelists[i] = (void *)1;
 #       endif
     }
     /* Set up the size 0 free lists.    */
@@ -108,6 +111,9 @@ GC_INNER void GC_init_thread_local(GC_tlfs p)
 #   ifdef GC_GCJ_SUPPORT
         p -> gcj_freelists[0] = ERROR_FL;
 #   endif
+#   ifdef ENABLE_DISCLAIM
+        p -> finalized_freelists[0] = (void *)1;
+#   endif
 }
 
 /* We hold the allocator lock.  */
@@ -280,6 +286,10 @@ GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
           if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
         }
 #     endif /* GC_GCJ_SUPPORT */
+#     ifdef ENABLE_DISCLAIM
+        q = p -> finalized_freelists[j];
+        if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
+#     endif
     }
 }
 
@@ -299,6 +309,10 @@ GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
             q = p -> gcj_freelists[j];
             if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
 #         endif /* GC_GCJ_SUPPORT */
+#         ifdef ENABLE_DISCLAIM
+            q = p -> finalized_freelists[j];
+            if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+#         endif
         }
     }
 #endif /* GC_ASSERTIONS */