# # add_file "for_mlc.c" # # add_file "include/gc_for.h" # # add_file "tests/for_bm.c" # # add_file "tests/for_test.c" # # patch "Makefile.am" # from [eec2e766a27fb6a213144ab5ec65559b703f05c8] # to [5c6a6ef2e527cbc644256e0d508ad2c4c19a3011] # # patch "Makefile.direct" # from [59e903bc132a95d30c40fe10050a6a8dad89c1c0] # to [916b3260dfb5b06f0247fc09f42005fb461b2513] # # patch "allchblk.c" # from [2bb814ae97c2f12def1843c7a89b1d99b635e7b2] # to [75847f719a5fd6c322502a53f73361db0f74a453] # # patch "configure.ac" # from [3c1441e126f3195772c2ae0bbeb2c9a255c67be2] # to [7d32164b1515c77e19f027c6436cfa51982ac2f7] # # patch "for_mlc.c" # from [] # to [32dd873855d11f916ac73f49648360de482ebf77] # # patch "include/gc_for.h" # from [] # to [f493572aa676103c7befac3acfe3cbd87e2c55a4] # # patch "include/gc_mark.h" # from [c162dacfde3a5dff4ceac1882df2ef6ed944e64e] # to [942ebec4a7ef50c8988fdfcb211fc8940fc8f413] # # patch "include/include.am" # from [e4136df32217795068ac79f9dcda8e58fe7b31b5] # to [1d0306d1752c6257710d19a2bcc7eea8e8b52668] # # patch "include/private/gc_priv.h" # from [3c942c5cca153b783c37b53d69dedfd4e694ac41] # to [9eeedfe73f4212e1321057d2b790734ba30cc93e] # # patch "mark.c" # from [b2ed6cde121b20ecced2134a7662e20037c5bc1c] # to [73d89ba1efe0cccd4227a511ef8876e83072f0a8] # # patch "misc.c" # from [60c0100f6484fe400c958bd2895ab12bb1da4927] # to [2e96da55e24e9765ee47668df60daccfcf219c30] # # patch "reclaim.c" # from [9345019f2f8d4e3cc3eda175d5b530a9c3043893] # to [4c9e494f9132b804e649993d7dcc5e100f2e3481] # # patch "tests/for_bm.c" # from [] # to [3dc968ea1746d898253467cd76ac251cbc793780] # # patch "tests/for_test.c" # from [] # to [584fac5d64c7862961afbcf912f6281269c55b83] # # patch "tests/tests.am" # from [f2460aee5c9854dd74a488ef3bbd524d1d1ff88b] # to [f3759f8140e85f0edf15b85c9ced68467e1cf27e] # --- Makefile.am +++ Makefile.am @@ -80,6 +80,14 @@ libgc_la_SOURCES += win32_threads.c endif +# Optional +# -------- + +if ENABLE_RECLAIM_NOTIFICATION +libgc_la_SOURCES += for_mlc.c +endif + + # Include THREADDLLIBS here to ensure that the correct versions of # linuxthread semaphore functions get linked: libgc_la_LIBADD = @addobjs@ $(THREADDLLIBS) $(UNWINDLIBS) --- Makefile.direct +++ Makefile.direct @@ -36,7 +36,7 @@ AO_SRC_DIR=$(srcdir)/libatomic_ops-$(AO_VERSION) 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_FINALIZE_ON_RECLAIM # To build the parallel collector on Linux, add to the above: # -DGC_LINUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC @@ -313,14 +313,14 @@ 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 thread_local_alloc.o + gc_dlopen.o backgraph.o win32_threads.o thread_local_alloc.o for_mlc.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 thread_local_alloc.c + backgraph.c win32_threads.c thread_local_alloc.c for_mlc.c CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c include/cord.h include/ec.h include/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC @@ -337,7 +337,7 @@ include/new_gc_alloc.h include/gc_allocator.h \ include/javaxfc.h sparc_sunos4_mach_dep.s sparc_netbsd_mach_dep.s \ include/gc_backptr.h \ - hpux_test_and_clear.s include/gc_gcj.h \ + hpux_test_and_clear.s include/gc_gcj.h include/gc_for.h \ include/private/dbg_mlc.h \ include/private/specific.h powerpc_darwin_mach_dep.s \ include/leak_detector.h include/gc_amiga_redirects.h \ --- allchblk.c +++ allchblk.c @@ -226,6 +226,12 @@ { word descr; size_t granules; +# ifdef ENABLE_RECLAIM_NOTIFICATION + if (GC_obj_kinds[kind].ok_reclaim_notifier_proc) + flags |= HAS_RECLAIM_NOTIFIER; + if (GC_obj_kinds[kind].ok_mark_unconditionally) + flags |= MARK_UNCONDITIONALLY; +# endif /* Set size, kind and mark proc fields */ hhdr -> hb_sz = byte_sz; --- configure.ac +++ configure.ac @@ -446,7 +446,17 @@ fi fi +AC_ARG_ENABLE(reclaim-notification, + [AC_HELP_STRING([--disable-reclaim-notification], + [Disable alternative (more efficient) finalization interface.])]) +if test x"$enable_reclaim_notification" != xno; then + AC_DEFINE(ENABLE_RECLAIM_NOTIFICATION, 1, + [Define to enable alternative finalization interface.]) +fi +AM_CONDITIONAL(ENABLE_RECLAIM_NOTIFICATION, + [test x"$enable_finalize_on_reclaim" != xno]) + dnl This is something of a hack. When cross-compiling we turn off dnl some functionality. We also enable the "small" configuration. dnl These is only correct when targetting an embedded system. FIXME. --- for_mlc.c +++ for_mlc.c @@ -0,0 +1,103 @@ +#ifdef ENABLE_RECLAIM_NOTIFICATION + +#include "gc_for.h" +#include "private/gc_priv.h" + + +/* Low level interface for reclaim callbacks. */ + +int GC_register_reclaim_notifier(int kind, + int (*notifier_proc)(void *obj, void *cd), + void *cd, + int mark_unconditionally) +{ + GC_obj_kinds[kind].ok_reclaim_notifier_proc = notifier_proc; + GC_obj_kinds[kind].ok_reclaim_notifier_cd = cd; + GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally; +} + + +/* High level interface for finalization. */ + +int GC_finalize_kind; + +int GC_finalize_debug_kind; + +ptr_t * GC_finalize_objfreelist; +ptr_t * GC_finalize_debugobjfreelist; + +static int GC_for_reclaim_notifier(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_finalize_malloc() +{ + DCL_LOCK_STATE; + if (done_init) + return; + GC_init(); + LOCK(); + if (done_init) + goto done; + done_init = 1; + + GC_finalize_objfreelist = (ptr_t *)GC_new_free_list_inner(); + GC_finalize_kind = + GC_new_kind_inner((void **)GC_finalize_objfreelist, + 0 | GC_DS_LENGTH, + TRUE, TRUE); + GC_register_reclaim_notifier_inner(GC_finalize_kind, + GC_for_reclaim_notifier, 0, 1); + +done: + UNLOCK(); +} + +ptr_t GC_clear_stack(); + +void *GC_malloc_with_finalizer(size_t lb, struct GC_finalizer_closure *fclos) +{ + 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_finalize_objfreelist[lg]; + FASTLOCK(); + if (EXPECT(!FASTLOCK_SUCCEEDED() || (op = *opp) == 0, 0)) { + FASTUNLOCK(); + op = GC_generic_malloc((word)lb, GC_finalize_kind); + } else { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES(lg); + FASTUNLOCK(); + } + } else + op = GC_generic_malloc((word)lb, GC_finalize_kind); + *(void **)op = (ptr_t)fclos + 1; /* See [1] */ + return GC_clear_stack(op + sizeof(void *)); +} + +#endif /* ENABLE_RECLAIM_NOTIFICATION */ + +/* [1] The meta-finalizer 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. */ --- include/gc_for.h +++ include/gc_for.h @@ -0,0 +1,16 @@ +#ifndef _GC_FOR_H +#define _GC_FOR_H + +#include "gc.h" + +struct GC_finalizer_closure { + void (*proc)(void *obj, void *cd); + void *cd; +}; + +GC_API void *GC_malloc_with_finalizer(size_t size, + struct GC_finalizer_closure *fc); + +GC_API void GC_init_finalize_malloc(void); + +#endif --- include/gc_mark.h +++ include/gc_mark.h @@ -170,6 +170,19 @@ int GC_new_proc(GC_mark_proc); int GC_new_proc_inner(GC_mark_proc); +/* 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 notifier_proc can be protected */ +/* with 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-reclaim-notification. */ +int GC_register_reclaim_notifier(int kind, + int (*notifier_proc)(void *obj, void *cd), + void *cd, + int mark_from_all); +#define GC_register_reclaim_notifier_inner GC_register_reclaim_notifier + /* Allocate an object of a given kind. Note that in multithreaded */ /* contexts, this is usually unsafe for kinds that have the descriptor */ /* in the object itself, since there is otherwise a window in which */ --- include/include.am +++ include/include.am @@ -33,6 +33,11 @@ include/gc_config_macros.h \ include/gc_tiny_fl.h +if ENABLE_RECLAIM_NOTIFICATION +pkginclude_HEADERS += include/gc_for.h +endif +EXTRA_DIST += include/gc_for.h + # headers which are not installed # dist_noinst_HEADERS += \ --- include/private/gc_priv.h +++ include/private/gc_priv.h @@ -634,6 +634,14 @@ /* before it can be reallocated. */ /* Only set with USE_MUNMAP. */ # define FREE_BLK 4 /* Block is free, i.e. not in use. */ +# ifdef ENABLE_RECLAIM_NOTIFICATION +# define HAS_RECLAIM_NOTIFIER 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. */ @@ -1039,6 +1047,18 @@ /* 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_RECLAIM_NOTIFICATION + GC_bool ok_mark_unconditionally; + /* Mark from all, including unmarked, objects */ + /* in block. Used to protect objects reachable */ + /* from reclaim notifiers. */ + int (*ok_reclaim_notifier_proc)(void *obj, void *cd); + void *ok_reclaim_notifier_cd; + /* The notifier procedure is called before obj */ + /* is reclaimed but must also handle 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)) --- mark.c +++ mark.c @@ -47,6 +47,9 @@ /* This must be done statically since they may be accessed before */ /* GC_init is called. */ /* It's done here, since we need to deal with mark descriptors. */ +/* The last fields, ok_reclaim_enter_proc, ok_reclaim_notifier_proc, */ +/* reclaim_leave_proc and ok_reclaim_cd, are implicitely cleared if */ +/* present. */ struct obj_kind GC_obj_kinds[MAXOBJKINDS] = { /* PTRFREE */ { &GC_aobjfreelist[0], 0 /* filled in dynamically */, 0 | GC_DS_LENGTH, FALSE, FALSE }, @@ -1743,6 +1746,33 @@ } } +#ifdef MARK_UNCONDITIONALLY +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 SMALL_CONFIG /* Test whether any page in the given block is dirty */ GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr) @@ -1820,11 +1850,19 @@ 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 + else 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)); } --- misc.c +++ misc.c @@ -1090,6 +1090,10 @@ GC_obj_kinds[result].ok_descriptor = descr; GC_obj_kinds[result].ok_relocate_descr = adjust; GC_obj_kinds[result].ok_init = clear; +# ifdef ENABLE_RECLAIM_NOTIFICATION + GC_obj_kinds[result].ok_reclaim_notifier_proc = 0; + GC_obj_kinds[result].ok_reclaim_notifier_cd = 0; +# endif return result; } --- reclaim.c +++ reclaim.c @@ -16,6 +16,7 @@ #include #include "private/gc_priv.h" +#include "gc_for.h" signed_word GC_bytes_found = 0; /* Number of bytes of memory reclaimed */ @@ -92,6 +93,11 @@ GC_bool GC_block_empty(hdr *hhdr) { + /* XXX: Only if reclaim notifiers have not been run. */ +# ifdef ENABLE_RECLAIM_NOTIFICATION + if (hhdr -> hb_flags & HAS_RECLAIM_NOTIFIER) + return FALSE; +# endif return (hhdr -> hb_n_marks == 0); } @@ -183,6 +189,57 @@ return(list); } +#ifdef ENABLE_RECLAIM_NOTIFICATION +/* 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_reclaim_with_notifiers(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 *); + void *cd = ok -> ok_reclaim_notifier_cd; + + GC_ASSERT(sz == hhdr -> hb_sz); + p = (word *)(hbp -> hb_body); + plim = (word *)((ptr_t)p + HBLKSIZE - sz); + + proc = ok -> ok_reclaim_notifier_proc; + 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_RECLAIM_NOTIFICATION */ + /* Don't really reclaim objects, just check for unmarked ones: */ /*ARGSUSED*/ void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz) @@ -217,6 +274,11 @@ GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr); GC_remove_protection(hbp, 1, (hhdr)->hb_descr == 0 /* Pointer-free? */); +# ifdef ENABLE_RECLAIM_NOTIFICATION + if (hhdr -> hb_flags & HAS_RECLAIM_NOTIFIER) + result = GC_reclaim_with_notifiers(hbp, hhdr, sz, list, count); + else +# endif if (init) { result = GC_reclaim_clear(hbp, hhdr, sz, list, count); } else { @@ -274,6 +336,18 @@ GC_add_leaked((ptr_t)hbp); } else { size_t blocks = OBJ_SZ_TO_BLOCKS(sz); +# ifdef ENABLE_RECLAIM_NOTIFICATION + if (EXPECT(hhdr->hb_flags & HAS_RECLAIM_NOTIFIER, 0)) { + struct obj_kind *ok = &GC_obj_kinds[hhdr->hb_obj_kind]; + int resurrect; + resurrect = (*ok->ok_reclaim_notifier_proc) + (hbp, ok->ok_reclaim_notifier_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; } @@ -281,6 +355,7 @@ GC_freehblk(hbp); } } else { + in_use: if (hhdr -> hb_descr != 0) { GC_composite_in_use += sz; } else { --- tests/for_bm.c +++ tests/for_bm.c @@ -0,0 +1,115 @@ +#include "gc_for.h" +#include +#include +#include +#include +#include + +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 +} +testobj_t testobj_new(int model) +{ + static struct GC_finalizer_closure fclos = { + testobj_finalize, + &nf + }; + 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_malloc_with_finalizer(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_finalize_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; +} --- tests/for_test.c +++ tests/for_test.c @@ -0,0 +1,114 @@ +/* Test that objects reachable from an object allocated with + * GC_malloc_with_finalizer is not reclaimable before the finalizer + * is called. */ + +#include +#include +#include +#include +#include + +typedef struct pair_s *pair_t; + +struct pair_s { + int is_valid; + pair_t car; + pair_t cdr; +}; + +void +pair_dct(void *obj, void *cd) +{ + pair_t p = obj; + assert(p->is_valid == 782); + assert(!p->car || p->car->is_valid); + assert(!p->cdr || p->cdr->is_valid); + p->is_valid = 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_malloc_with_finalizer(sizeof(struct pair_s), &fc); + p->is_valid = 782; + p->car = car; + p->cdr = cdr; + return p; +} + +void +pair_check_rec(pair_t p) +{ + while (p) { + assert(p->is_valid == 782); + if (rand() % 2) + p = p->car; + else + p = p->cdr; + } +} + +#define THREAD_CNT 6 + +#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_finalize_malloc(); +#if THREAD_CNT > 1 + 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 + test(NULL); +#endif + return 0; +} + --- tests/tests.am +++ tests/tests.am @@ -22,6 +22,8 @@ $(THREADLIBS) $(UNWINDLIBS) $(EXTRA_TEST_LIBS) +# Details on each test +# -------------------- TESTS += gctest check_PROGRAMS += gctest @@ -58,3 +60,14 @@ test_cpp_LDADD = libgccpp.la $(test_ldadd) endif +if ENABLE_RECLAIM_NOTIFICATION +TESTS += for_test +check_PROGRAMS += for_test +for_test_SOURCES = tests/for_test.c +for_test_LDADD = $(test_ldadd) + +TESTS += for_bm +check_PROGRAMS += for_bm +for_bm_SOURCES = tests/for_bm.c +for_bm_LDADD = $(test_ldadd) +endif