Commit fe652a8b authored by aljee@hyper.cx's avatar aljee@hyper.cx Committed by ian@well-typed.com

Maintain per-generation lists of weak pointers (#7847)

parent 6770663f
......@@ -83,6 +83,8 @@ typedef struct generation_ {
StgTSO * threads; // threads in this gen
// linked via global_link
StgWeak * weak_ptr_list; // weak pointers in this gen
struct generation_ *to; // destination gen for live objects
// stats information
......@@ -116,6 +118,7 @@ typedef struct generation_ {
bdescr * bitmap; // bitmap for compacting collection
StgTSO * old_threads;
StgWeak * old_weak_ptr_list;
} generation;
extern generation * generations;
......
......@@ -465,7 +465,6 @@ extern StgWord stg_stack_save_entries[];
// Storage.c
extern unsigned int RTS_VAR(g0);
extern unsigned int RTS_VAR(large_alloc_lim);
extern StgWord RTS_VAR(weak_ptr_list);
extern StgWord RTS_VAR(atomic_modify_mutvar_mutex);
// RtsFlags
......
......@@ -380,8 +380,8 @@ stg_mkWeakzh ( gcptr key,
StgWeak_cfinalizers(w) = stg_NO_FINALIZER_closure;
ACQUIRE_LOCK(sm_mutex);
StgWeak_link(w) = W_[weak_ptr_list];
W_[weak_ptr_list] = w;
StgWeak_link(w) = generation_weak_ptr_list(W_[g0]);
generation_weak_ptr_list(W_[g0]) = w;
RELEASE_LOCK(sm_mutex);
IF_DEBUG(weak, ccall debugBelch(stg_weak_msg,w));
......
......@@ -1767,9 +1767,11 @@ computeRetainerSet( void )
//
// The following code assumes that WEAK objects are considered to be roots
// for retainer profilng.
for (weak = weak_ptr_list; weak != NULL; weak = weak->link)
// retainRoot((StgClosure *)weak);
retainRoot(NULL, (StgClosure **)&weak);
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
for (weak = generations[g].weak_ptr_list; weak != NULL; weak = weak->link)
// retainRoot((StgClosure *)weak);
retainRoot(NULL, (StgClosure **)&weak);
}
// Consider roots from the stable ptr table.
markStableTables(retainRoot, NULL);
......
......@@ -295,6 +295,8 @@ hs_add_root(void (*init_root)(void) STG_UNUSED)
static void
hs_exit_(rtsBool wait_foreign)
{
nat g;
if (hs_init_count <= 0) {
errorBelch("warning: too many hs_exit()s");
return;
......@@ -325,7 +327,9 @@ hs_exit_(rtsBool wait_foreign)
exitScheduler(wait_foreign);
/* run C finalizers for all active weak pointers */
runAllCFinalizers(weak_ptr_list);
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
runAllCFinalizers(generations[g].weak_ptr_list);
}
#if defined(RTS_USER_SIGNALS)
if (RtsFlags.MiscFlags.install_signal_handlers) {
......
......@@ -16,8 +16,6 @@
#include "Prelude.h"
#include "Trace.h"
StgWeak *weak_ptr_list;
void
runCFinalizers(StgCFinalizerList *list)
{
......
......@@ -14,7 +14,7 @@
#include "BeginPrivate.h"
extern rtsBool running_finalizers;
extern StgWeak * weak_ptr_list;
extern StgWeak * dead_weak_ptr_list;
void runCFinalizers(StgCFinalizerList *list);
void runAllCFinalizers(StgWeak *w);
......
......@@ -917,11 +917,13 @@ compact(StgClosure *static_objects)
markScheduler((evac_fn)thread_root, NULL);
// the weak pointer lists...
if (weak_ptr_list != NULL) {
thread((void *)&weak_ptr_list);
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
if (generations[g].weak_ptr_list != NULL)
thread((void *)&generations[g].weak_ptr_list);
}
if (old_weak_ptr_list != NULL) {
thread((void *)&old_weak_ptr_list); // tmp
if (dead_weak_ptr_list != NULL) {
thread((void *)&dead_weak_ptr_list); // tmp
}
// mutable lists
......
......@@ -699,7 +699,7 @@ GarbageCollect (nat collect_gen,
// Start any pending finalizers. Must be after
// updateStableTables() and stableUnlock() (see #4221).
RELEASE_SM_LOCK;
scheduleFinalizers(cap, old_weak_ptr_list);
scheduleFinalizers(cap, dead_weak_ptr_list);
ACQUIRE_SM_LOCK;
// check sanity after GC
......
......@@ -75,34 +75,37 @@
typedef enum { WeakPtrs, WeakThreads, WeakDone } WeakStage;
static WeakStage weak_stage;
/* Weak pointers
*/
StgWeak *old_weak_ptr_list; // also pending finaliser list
StgWeak *weak_ptr_list_tail;
// List of weak pointers whose key is dead
StgWeak *dead_weak_ptr_list;
// List of threads found to be unreachable
StgTSO *resurrected_threads;
static void collectDeadWeakPtrs (generation *gen);
static rtsBool tidyWeakList (generation *gen);
static void resurrectUnreachableThreads (generation *gen);
static rtsBool tidyThreadList (generation *gen);
void
initWeakForGC(void)
{
old_weak_ptr_list = weak_ptr_list;
weak_ptr_list = NULL;
weak_ptr_list_tail = NULL;
nat g;
for (g = 0; g <= N; g++) {
generation *gen = &generations[g];
gen->old_weak_ptr_list = gen->weak_ptr_list;
gen->weak_ptr_list = NULL;
}
weak_stage = WeakPtrs;
dead_weak_ptr_list = NULL;
resurrected_threads = END_TSO_QUEUE;
}
rtsBool
traverseWeakPtrList(void)
{
StgWeak *w, **last_w, *next_w;
StgClosure *new;
rtsBool flag = rtsFalse;
const StgInfoTable *info;
switch (weak_stage) {
......@@ -110,73 +113,23 @@ traverseWeakPtrList(void)
return rtsFalse;
case WeakPtrs:
/* doesn't matter where we evacuate values/finalizers to, since
* these pointers are treated as roots (iff the keys are alive).
*/
gct->evac_gen_no = 0;
last_w = &old_weak_ptr_list;
for (w = old_weak_ptr_list; w != NULL; w = next_w) {
/* There might be a DEAD_WEAK on the list if finalizeWeak# was
* called on a live weak pointer object. Just remove it.
*/
if (w->header.info == &stg_DEAD_WEAK_info) {
next_w = w->link;
*last_w = next_w;
continue;
}
info = get_itbl((StgClosure *)w);
switch (info->type) {
case WEAK:
/* Now, check whether the key is reachable.
*/
new = isAlive(w->key);
if (new != NULL) {
w->key = new;
// evacuate the value and finalizer
evacuate(&w->value);
evacuate(&w->finalizer);
// remove this weak ptr from the old_weak_ptr list
*last_w = w->link;
next_w = w->link;
// and put it on the new weak ptr list.
if (weak_ptr_list == NULL) {
weak_ptr_list = w;
} else {
weak_ptr_list_tail->link = w;
}
weak_ptr_list_tail = w;
w->link = NULL;
flag = rtsTrue;
debugTrace(DEBUG_weak,
"weak pointer still alive at %p -> %p",
w, w->key);
continue;
}
else {
last_w = &(w->link);
next_w = w->link;
continue;
}
default:
barf("traverseWeakPtrList: not WEAK");
}
{
nat g;
for (g = 0; g <= N; g++) {
if (tidyWeakList(&generations[g])) {
flag = rtsTrue;
}
}
/* If we didn't make any changes, then we can go round and kill all
* the dead weak pointers. The old_weak_ptr list is used as a list
* the dead weak pointers. The dead_weak_ptr list is used as a list
* of pending finalizers later on.
*/
if (flag == rtsFalse) {
for (w = old_weak_ptr_list; w; w = w->link) {
evacuate(&w->finalizer);
}
for (g = 0; g <= N; g++) {
collectDeadWeakPtrs(&generations[g]);
}
// Next, move to the WeakThreads stage after fully
// scavenging the finalizers we've just evacuated.
......@@ -184,6 +137,7 @@ traverseWeakPtrList(void)
}
return rtsTrue;
}
case WeakThreads:
/* Now deal with the step->threads lists, which behave somewhat like
......@@ -229,6 +183,17 @@ traverseWeakPtrList(void)
}
}
static void collectDeadWeakPtrs (generation *gen)
{
StgWeak *w, *next_w;
for (w = gen->old_weak_ptr_list; w != NULL; w = next_w) {
evacuate(&w->finalizer);
next_w = w->link;
w->link = dead_weak_ptr_list;
dead_weak_ptr_list = w;
}
}
static void resurrectUnreachableThreads (generation *gen)
{
StgTSO *t, *tmp, *next;
......@@ -253,6 +218,80 @@ traverseWeakPtrList(void)
}
}
static rtsBool tidyWeakList(generation *gen)
{
StgWeak *w, **last_w, *next_w;
const StgInfoTable *info;
StgClosure *new;
rtsBool flag = rtsFalse;
last_w = &gen->old_weak_ptr_list;
for (w = gen->old_weak_ptr_list; w != NULL; w = next_w) {
/* There might be a DEAD_WEAK on the list if finalizeWeak# was
* called on a live weak pointer object. Just remove it.
*/
if (w->header.info == &stg_DEAD_WEAK_info) {
next_w = w->link;
*last_w = next_w;
continue;
}
info = get_itbl((StgClosure *)w);
switch (info->type) {
case WEAK:
/* Now, check whether the key is reachable.
*/
new = isAlive(w->key);
if (new != NULL) {
generation *new_gen;
w->key = new;
// Find out which generation this weak ptr is in, and
// move it onto the weak ptr list of that generation.
new_gen = Bdescr((P_)w)->gen;
gct->evac_gen_no = new_gen->no;
// evacuate the value and finalizer
evacuate(&w->value);
evacuate(&w->finalizer);
// remove this weak ptr from the old_weak_ptr list
*last_w = w->link;
next_w = w->link;
// and put it on the correct weak ptr list.
w->link = new_gen->weak_ptr_list;
new_gen->weak_ptr_list = w;
flag = rtsTrue;
if (gen->no != new_gen->no) {
debugTrace(DEBUG_weak,
"moving weak pointer %p from %d to %d",
w, gen->no, new_gen->no);
}
debugTrace(DEBUG_weak,
"weak pointer still alive at %p -> %p",
w, w->key);
continue;
}
else {
last_w = &(w->link);
next_w = w->link;
continue;
}
default:
barf("tidyWeakList: not WEAK: %d, %p", info->type, w);
}
}
return flag;
}
static rtsBool tidyThreadList (generation *gen)
{
StgTSO *t, *tmp, *next, **prev;
......@@ -303,38 +342,40 @@ static rtsBool tidyThreadList (generation *gen)
/* -----------------------------------------------------------------------------
Evacuate every weak pointer object on the weak_ptr_list, and update
the link fields.
ToDo: with a lot of weak pointers, this will be expensive. We
should have a per-GC weak pointer list, just like threads.
-------------------------------------------------------------------------- */
void
markWeakPtrList ( void )
{
StgWeak *w, **last_w;
nat g;
for (g = 0; g <= N; g++) {
generation *gen = &generations[g];
StgWeak *w, **last_w;
last_w = &weak_ptr_list;
for (w = weak_ptr_list; w; w = w->link) {
// w might be WEAK, EVACUATED, or DEAD_WEAK (actually CON_STATIC) here
last_w = &gen->weak_ptr_list;
for (w = gen->weak_ptr_list; w != NULL; w = w->link) {
// w might be WEAK, EVACUATED, or DEAD_WEAK (actually CON_STATIC) here
#ifdef DEBUG
{ // careful to do this assertion only reading the info ptr
// once, because during parallel GC it might change under our feet.
const StgInfoTable *info;
info = w->header.info;
ASSERT(IS_FORWARDING_PTR(info)
|| info == &stg_DEAD_WEAK_info
|| INFO_PTR_TO_STRUCT(info)->type == WEAK);
}
{ // careful to do this assertion only reading the info ptr
// once, because during parallel GC it might change under our feet.
const StgInfoTable *info;
info = w->header.info;
ASSERT(IS_FORWARDING_PTR(info)
|| info == &stg_DEAD_WEAK_info
|| INFO_PTR_TO_STRUCT(info)->type == WEAK);
}
#endif
evacuate((StgClosure **)last_w);
w = *last_w;
if (w->header.info == &stg_DEAD_WEAK_info) {
last_w = &(w->link);
} else {
last_w = &(w->link);
}
}
evacuate((StgClosure **)last_w);
w = *last_w;
if (w->header.info == &stg_DEAD_WEAK_info) {
last_w = &(w->link);
} else {
last_w = &(w->link);
}
}
}
}
......@@ -93,6 +93,8 @@ initGeneration (generation *gen, int g)
#endif
gen->threads = END_TSO_QUEUE;
gen->old_threads = END_TSO_QUEUE;
gen->weak_ptr_list = NULL;
gen->old_weak_ptr_list = NULL;
}
void
......@@ -169,7 +171,6 @@ initStorage (void)
generations[0].max_blocks = 0;
weak_ptr_list = NULL;
caf_list = END_OF_STATIC_LIST;
revertible_caf_list = END_OF_STATIC_LIST;
......
......@@ -346,6 +346,7 @@ wanteds = concat
,structSize C "generation"
,structField C "generation" "n_new_large_words"
,structField C "generation" "weak_ptr_list"
,structSize Both "CostCentreStack"
,structField C "CostCentreStack" "ccsID"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment