/home/urkedal/proj/bdwgc
diff --git a/Makefile.am b/Makefile.am
index fb658e2..3e583c9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,6 +71,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 = ./atomic_ops.c 	 
 endif 	 
diff --git a/Makefile.direct b/Makefile.direct
index 8100344..d6c2399 100644
--- a/Makefile.direct
+++ b/Makefile.direct
@@ -36,7 +36,7 @@ AO_VERSION=1.2
 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_DISCLAIM
 
 # To build the parallel collector on Linux, add to the above:
 # -DGC_LINUX_THREADS -DPARALLEL_MARK -DTHREAD_LOCAL_ALLOC
@@ -341,14 +341,14 @@ 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 thread_local_alloc.o
+  gc_dlopen.o backgraph.o win32_threads.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 thread_local_alloc.c
+  backgraph.c win32_threads.c thread_local_alloc.c disclaim.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
 
@@ -365,7 +365,7 @@ SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.S \
     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_disclaim.h \
     include/private/dbg_mlc.h \
     include/private/specific.h \
     include/leak_detector.h include/gc_amiga_redirects.h \
diff --git a/README.disclaim b/README.disclaim
new file mode 100644
index 0000000..bb95cbe
--- /dev/null
+++ b/README.disclaim
@@ -0,0 +1,14 @@
+== Applying the Disclaim Patch ==
+
+Pick the patch closest in date to the version of GC you want to patch.
+
+1.  Unpack the source code, or make a copy of the CVS tree.
+
+2.  From the top level source directory, issue 'patch -p1 <PATCHFILE.patch'.
+    If it doesn't apply cleanly, you should try another GC/patch combination.
+
+3.  IMPORTANT: Run 'autoreconf -vi'.  This regenerates auto-generated files
+    which are not included in the patch.
+
+4.  Build, test and install the collector.  It's a very good idea to run
+    'make check' before installing.
diff --git a/allchblk.c b/allchblk.c
index b723a7f..8da93a1 100644
--- a/allchblk.c
+++ b/allchblk.c
@@ -219,6 +219,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;
diff --git a/configure.ac b/configure.ac
index 8bb800f..21ff407 100644
--- a/configure.ac
+++ b/configure.ac
@@ -580,6 +580,16 @@ if test "${enable_redirect_malloc}" = yes; then
     AC_DEFINE(GC_USE_DLOPEN_WRAP)
 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
index 0000000..587e6c2
--- /dev/null
+++ b/disclaim.c
@@ -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(GC_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
index 0000000..c67a9ce
--- /dev/null
+++ b/include/gc_disclaim.h
@@ -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
diff --git a/include/include.am b/include/include.am
index 399e542..eac5867 100644
--- a/include/include.am
+++ b/include/include.am
@@ -34,6 +34,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 += \
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index 616173f..41ece81 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -708,6 +708,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.   */
@@ -1144,6 +1152,18 @@ 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))
diff --git a/include/private/thread_local_alloc.h b/include/private/thread_local_alloc.h
index 32b6a3e..1f688a7 100644
--- a/include/private/thread_local_alloc.h
+++ b/include/private/thread_local_alloc.h
@@ -87,6 +87,9 @@ typedef struct thread_local_freelists {
 		/* Don't use local free lists for up to this much 	*/
 		/* allocation.						*/
 
+#       ifdef ENABLE_DISCLAIM
+	  void * finalized_freelists[TINY_FREELISTS];
+#       endif
 #   endif
 } *GC_tlfs;
 
diff --git a/mark.c b/mark.c
index da9dc1c..b7b6177 100644
--- a/mark.c
+++ b/mark.c
@@ -1793,6 +1793,36 @@ 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 SMALL_CONFIG
 /* Test whether any page in the given block is dirty	*/
 STATIC GC_bool GC_block_was_dirty(struct hblk *h, hdr *hhdr)
@@ -1873,11 +1903,19 @@ 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 d8fc82b..2b11e61 100644
--- a/misc.c
+++ b/misc.c
@@ -1188,6 +1188,10 @@ unsigned GC_new_kind_inner(void **fl, GC_word descr, int adjust, int clear)
     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;
 }
 
diff --git a/reclaim.c b/reclaim.c
index 12afa91..a70e0fc 100644
--- a/reclaim.c
+++ b/reclaim.c
@@ -16,6 +16,7 @@
 
 #include <stdio.h>
 #include "private/gc_priv.h"
+#include "gc_disclaim.h"
 
 signed_word GC_bytes_found = 0;
 			/* Number of bytes of memory reclaimed     */
@@ -38,6 +39,10 @@ STATIC unsigned GC_n_leaked = 0;
 
 GC_bool GC_have_errors = FALSE;
 
+#if !defined(EAGER_SWEEP) && defined(MARK_UNCONDITIONALLY)
+void GC_reclaim_unconditionally_marked(void);
+#endif
+
 STATIC void GC_add_leaked(ptr_t leaked)
 {
     if (GC_n_leaked < MAX_LEAKED) {
@@ -94,6 +99,11 @@ void GC_print_all_errors (void)
  
 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);
 }
 
@@ -183,6 +193,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)
 {
@@ -216,6 +276,11 @@ ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz,
 
     GC_ASSERT(GC_find_header((ptr_t)hbp) == hhdr);
     GC_remove_protection(hbp, 1, (hhdr)->hb_descr == 0 /* Pointer-free? */);
+#   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);
     } else {
@@ -273,6 +338,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;
 	      }
@@ -280,6 +356,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 {
@@ -521,6 +598,11 @@ void GC_start_reclaim(GC_bool report_if_found)
     /* This is a very stupid thing to do.  We make it possible anyway,	*/
     /* so that you can convince yourself that it really is very stupid.	*/
     GC_reclaim_all((GC_stop_func)0, FALSE);
+# elif defined(MARK_UNCONDITIONALLY)
+    /* However, make sure to clear reclaimable objects of kinds with	*/
+    /* unconditional marking enabled before we do any significant	*/
+    /* marking work.							*/
+    GC_reclaim_unconditionally_marked();
 # endif
 # if defined(PARALLEL_MARK)
     GC_ASSERT(0 == GC_fl_builder_count);
@@ -607,3 +689,33 @@ GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old)
 #   endif
     return(TRUE);
 }
+
+#if !defined(EAGER_SWEEP) && defined(MARK_UNCONDITIONALLY)
+/* Part of disclaim patch.  This could be merged with the above, but I don't
+ * want to clobber the original source too much by changing the prototype and
+ * testing for defined(MARK_UNCONDITIONALLY). */
+void GC_reclaim_unconditionally_marked(void)
+{
+    word sz;
+    unsigned kind;
+    hdr * hhdr;
+    struct hblk * hbp;
+    struct obj_kind * ok;
+    struct hblk ** rlp;
+    struct hblk ** rlh;
+    for (kind = 0; kind < GC_n_kinds; kind++) {
+	ok = &(GC_obj_kinds[kind]);
+	if (!ok->ok_mark_unconditionally) continue;
+	rlp = ok->ok_reclaim_list;
+	if (rlp == 0) continue;
+	for (sz = 1; sz <= MAXOBJGRANULES; sz++) {
+	    rlh = rlp + sz;
+	    while ((hbp = *rlh) != 0) {
+		hhdr = HDR(hbp);
+		*rlh = hhdr->hb_next;
+		GC_reclaim_small_nonempty_block(hbp, FALSE);
+	    }
+	}
+    }
+}
+#endif
diff --git a/tests/disclaim_bench.c b/tests/disclaim_bench.c
new file mode 100644
index 0000000..1c90d83
--- /dev/null
+++ b/tests/disclaim_bench.c
@@ -0,0 +1,116 @@
+#include "gc_disclaim.h"
+#include "atomic_ops.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static AO_t free_count = 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)
+    AO_fetch_and_add1((AO_t *)carg);
+    assert(obj->i++ == 109);
+#undef obj
+}
+static struct GC_finalizer_closure fclos = {
+    testobj_finalize,
+    &free_count
+};
+
+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, &free_count,
+					   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();
+    }
+    assert(obj->i == 0 && obj->keep_link == NULL);
+    obj->i = 109;
+    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],
+	       free_count/(double)ALLOC_CNT, t, t/free_count);
+    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
index 0000000..bc8462a
--- /dev/null
+++ b/tests/disclaim_test.c
@@ -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;
+}
+
diff --git a/tests/tests.am b/tests/tests.am
index a9c0e73..14b26cd 100644
--- a/tests/tests.am
+++ b/tests/tests.am
@@ -19,6 +19,8 @@
 # Common libs to _LDADD for all tests.
 test_ldadd = $(top_builddir)/libgc.la $(EXTRA_TEST_LIBS)
 
+# Details on each test
+# --------------------
 
 TESTS += gctest$(EXEEXT)
 check_PROGRAMS += gctest
@@ -69,3 +71,14 @@ 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
diff --git a/thread_local_alloc.c b/thread_local_alloc.c
index 4d907fe..680bfb4 100644
--- a/thread_local_alloc.c
+++ b/thread_local_alloc.c
@@ -94,6 +94,9 @@ void GC_init_thread_local(GC_tlfs p)
 #	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.	*/
     /* We now handle most of them like regular free lists, to ensure	*/
@@ -104,6 +107,9 @@ 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
 }
 
 #ifdef GC_GCJ_SUPPORT
@@ -304,6 +310,10 @@ 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
     }
 }
 
@@ -323,6 +333,10 @@ 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 */
