(fix of commits
73d30d2b4,
e5fb574cf)
* README.md (Incremental Collection): Add note about bugs caused by
a missing GC_reachable_here call.
* doc/gcdescr.md (Generational Collection): Mention GC_reachable_here
for MANUAL_VDB mode.
* finalize.c (GC_register_disappearing_link_inner,
GC_register_finalizer_inner): Move GC_dirty(new_dl) call to be before
unlocking (so that to ensure no collection occurs between initialization
of new_dl and GC_dirty() call).
* finalize.c (GC_finalize): Call GC_dirty() immediately after updating
GC_fnlz_roots.fo_head (instead of setting needs_barrier) if
GC_object_finalized_proc is set.
* gcj_mlc.c (GC_gcj_malloc, GC_debug_gcj_malloc,
GC_gcj_malloc_ignore_off_page): Call
REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr) after GC_dirty(op).
* include/gc.h (GC_end_stubborn_change): Mention GC_reachable_here
in comment.
* include/gc_inline.h (GC_FAST_MALLOC_GRANS): Call
GC_reachable_here(next) after GC_end_stubborn_change(my_fl); remove
GC_end_stubborn_change() call when a non-pointer is stored to my_fl;
remove GC_end_stubborn_change() after GC_generic_malloc_many() call.
* include/gc_inline.h (GC_CONS): Call GC_reachable_here for the stored
pointers after GC_end_stubborn_change call.
* include/private/gc_priv.h (REACHABLE_AFTER_DIRTY): New macro.
* mallocx.c [MANUAL_VDB] (GC_generic_malloc_many): If
GC_is_heap_ptr(result) then call GC_dirty(result) and
REACHABLE_AFTER_DIRTY(op) after storing op pointer.
* typd_mlc.c (GC_make_sequence_descriptor): Call REACHABLE_AFTER_DIRTY
for the stored pointers after GC_dirty(result).
* typd_mlc.c (GC_malloc_explicitly_typed,
GC_malloc_explicitly_typed_ignore_off_page, GC_calloc_explicitly_typed):
Call REACHABLE_AFTER_DIRTY(d) after GC_dirty(op).
* win32_threads.c (GC_CreateThread, GC_beginthreadex,
GC_pthread_create): Call REACHABLE_AFTER_DIRTY for the stored pointer
after GC_dirty.
2. Information supplied by the programmer. The object is considered dirty
after a call to `GC_end_stubborn_change` provided the library has been
compiled suitably. It is typically not worth using for short-lived objects.
- Note that bugs caused by a missing `GC_end_stubborn_change` call are
- likely to be observed very infrequently and hard to trace.
+ Note that bugs caused by a missing `GC_end_stubborn_change` or
+ `GC_reachable_here` call are likely to be observed very infrequently and
+ hard to trace.
## Bugs
* (`PCR_VDB`) By relying on an external dirty bit implementation, in this
case the one in Xerox PCR.
* (`MANUAL_VDB`) Through explicit mutator cooperation. This requires the
- client code to call `GC_end_stubborn_change`, and is rarely used.
+ client code to call `GC_end_stubborn_change` (followed by a number of
+ `GC_reachable_here` calls), and is rarely used.
* (`DEFAULT_VDB`) By treating all pages as dirty. This is the default
if none of the other techniques is known to be usable. (Practical only for
testing.)
new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
dl_set_next(new_dl, dl_hashtbl -> head[index]);
+ GC_dirty(new_dl);
dl_hashtbl -> head[index] = new_dl;
dl_hashtbl -> entries++;
GC_dirty(dl_hashtbl->head + index);
UNLOCK();
- GC_dirty(new_dl);
return GC_SUCCESS;
}
new_fo -> fo_object_size = hhdr -> hb_sz;
new_fo -> fo_mark_proc = mp;
fo_set_next(new_fo, GC_fnlz_roots.fo_head[index]);
+ GC_dirty(new_fo);
GC_fo_entries++;
GC_fnlz_roots.fo_head[index] = new_fo;
GC_dirty(GC_fnlz_roots.fo_head + index);
UNLOCK();
- GC_dirty(new_fo);
}
GC_API void GC_CALL GC_register_finalizer(void * obj,
next_fo = fo_next(curr_fo);
if (NULL == prev_fo) {
GC_fnlz_roots.fo_head[i] = next_fo;
- needs_barrier = TRUE;
+ if (GC_object_finalized_proc) {
+ GC_dirty(GC_fnlz_roots.fo_head + i);
+ } else {
+ needs_barrier = TRUE;
+ }
} else {
fo_set_next(prev_fo, next_fo);
GC_dirty(prev_fo);
*(void **)op = ptr_to_struct_containing_descr;
UNLOCK();
GC_dirty(op);
- return((void *) op);
+ REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr);
+ return (void *)op;
}
/* Similar to GC_gcj_malloc, but add debug info. This is allocated */
result = GC_store_debug_info_inner(result, (word)lb, s, i);
UNLOCK();
GC_dirty(result);
+ REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr);
return result;
}
*(void **)op = ptr_to_struct_containing_descr;
UNLOCK();
GC_dirty(op);
- return((void *) op);
+ REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr);
+ return (void *)op;
}
#endif /* GC_GCJ_SUPPORT */
/* Only non-NULL pointer stores into the object are considered to be */
/* changes. Matters only if the library has been compiled with */
/* MANUAL_VDB defined (otherwise the function does nothing). */
+/* Should be followed typically by GC_reachable_here called for each */
+/* of the stored pointers. */
GC_API void GC_CALL GC_end_stubborn_change(const void *) GC_ATTR_NONNULL(1);
/* Return a pointer to the base (lowest address) of an object given */
GC_FAST_M_AO_STORE(my_fl, next); \
init; \
GC_PREFETCH_FOR_WRITE(next); \
- if ((kind) != GC_I_PTRFREE) GC_end_stubborn_change(my_fl); \
+ if ((kind) != GC_I_PTRFREE) { \
+ GC_end_stubborn_change(my_fl); \
+ GC_reachable_here(next); \
+ } \
GC_ASSERT(GC_size(result) >= (granules)*GC_GRANULE_BYTES); \
GC_ASSERT((kind) == GC_I_PTRFREE \
|| ((GC_word *)result)[1] == 0); \
/* Small counter value, not NULL */ \
GC_FAST_M_AO_STORE(my_fl, (char *)my_entry \
+ (granules) + 1); \
- if ((kind) != GC_I_PTRFREE) GC_end_stubborn_change(my_fl); \
result = (default_expr); \
break; \
} else { \
GC_generic_malloc_many(((granules) == 0? GC_GRANULE_BYTES : \
GC_RAW_BYTES_FROM_INDEX(granules)), \
kind, my_fl); \
- GC_end_stubborn_change(my_fl); \
my_entry = *my_fl; \
if (my_entry == 0) { \
result = (*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES); \
*(void **)(result) = (void *)(first); \
((void **)(result))[1] = (void *)(second); \
GC_end_stubborn_change(result); \
+ GC_reachable_here(first); \
+ GC_reachable_here(second); \
} \
} while (0)
#ifdef MANUAL_VDB
GC_INNER void GC_dirty_inner(const void *p); /* does not require locking */
# define GC_dirty(p) (GC_incremental ? GC_dirty_inner(p) : (void)0)
+# define REACHABLE_AFTER_DIRTY(p) GC_reachable_here(p)
#else
# define GC_dirty(p) (void)(p)
+# define REACHABLE_AFTER_DIRTY(p) (void)(p)
#endif
/* Same as GC_base but excepts and returns a pointer to const object. */
if (EXPECT(0 != op, TRUE))
obj_link(op) = 0;
*result = op;
+# ifdef MANUAL_VDB
+ if (GC_is_heap_ptr(result)) {
+ GC_dirty(result);
+ REACHABLE_AFTER_DIRTY(op);
+ }
+# endif
return;
}
GC_ASSERT(k < MAXOBJKINDS);
result -> sd_first = first;
result -> sd_second = second;
GC_dirty(result);
+ REACHABLE_AFTER_DIRTY(first);
+ REACHABLE_AFTER_DIRTY(second);
}
return((complex_descriptor *)result);
}
lg = BYTES_TO_GRANULES(GC_size(op));
op[GRANULES_TO_WORDS(lg) - 1] = d;
GC_dirty(op + GRANULES_TO_WORDS(lg) - 1);
+ REACHABLE_AFTER_DIRTY(d);
return op;
}
}
((word *)op)[GRANULES_TO_WORDS(lg) - 1] = d;
GC_dirty(op + GRANULES_TO_WORDS(lg) - 1);
+ REACHABLE_AFTER_DIRTY(d);
return op;
}
op[lw - 1] = (word)complex_descr;
GC_dirty(op + lw - 1);
+ REACHABLE_AFTER_DIRTY(complex_descr);
+
/* Make sure the descriptor is cleared once there is any danger */
/* it may have been collected. */
if (EXPECT(GC_general_register_disappearing_link(
args -> start = lpStartAddress;
args -> param = lpParameter;
GC_dirty(args);
+ REACHABLE_AFTER_DIRTY(lpParameter);
set_need_to_lock();
thread_h = CreateThread(lpThreadAttributes, dwStackSize, GC_win32_start,
args -> start = (LPTHREAD_START_ROUTINE)start_address;
args -> param = arglist;
GC_dirty(args);
+ REACHABLE_AFTER_DIRTY(arglist);
set_need_to_lock();
thread_h = _beginthreadex(security, stack_size,
si -> start_routine = start_routine;
si -> arg = arg;
GC_dirty(si);
+ REACHABLE_AFTER_DIRTY(arg);
if (attr != 0 &&
pthread_attr_getdetachstate(attr, &si->detached)
== PTHREAD_CREATE_DETACHED) {