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
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
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 \
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 \
# 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;
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])])
--- /dev/null
+#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. */
--- /dev/null
+/*
+ * 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
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 += \
/* 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. */
/* 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))
# 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)
}
}
+#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)
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));
}
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;
}
*/
#include "private/gc_priv.h"
+#include "gc_disclaim.h"
#include <stdio.h>
/* 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);
}
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)
{
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);
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;
}
GC_freehblk(hbp);
}
} else {
+ in_use:
if (hhdr -> hb_descr != 0) {
GC_composite_in_use += sz;
} else {
--- /dev/null
+#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;
+}
--- /dev/null
+/* 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;
+}
+
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
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. */
# 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. */
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
}
}
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 */