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