diff options
author | Simon Marlow <marlowsd@gmail.com> | 2015-06-01 21:34:02 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2015-06-01 21:34:02 +0100 |
commit | dfdc50d666498c5a1118557d67209fe067c61cc1 (patch) | |
tree | 0009f57ff2a980d636ac4730368485a0fa06711a | |
parent | f82e866504259c674d6fb3f66e67ae943a688b3f (diff) | |
download | haskell-dfdc50d666498c5a1118557d67209fe067c61cc1.tar.gz |
Don't call DEAD_WEAK finalizer again on shutdown (#7170)
Summary:
There's a race condition like this:
# A foreign pointer gets promoted to the last generation
# It has its finalizer called manually
# We start shutting down the runtime in `hs_exit_` from the main
thread
# A minor GC starts running (`scheduleDoGC`) on one of the threads
# The minor GC notices that we're in `SCHED_INTERRUPTING` state and
advances to `SCHED_SHUTTING_DOWN`
# The main thread tries to do major GC (with `scheduleDoGC`), but it
exits early because we're in `SCHED_SHUTTING_DOWN` state
# We end up with a `DEAD_WEAK` left on the list of weak pointers of
the last generation, because it relied on major GC removing it from
that list
This change:
* Ignores DEAD_WEAK finalizers when shutting down
* Makes the major GC on shutdown more likely
* Fixes a bogus assert
Test Plan:
before this diff https://ghc.haskell.org/trac/ghc/ticket/7170#comment:5
reproduced and after it doesn't
Reviewers: ezyang, austin, simonmar
Reviewed By: simonmar
Subscribers: bgamari, thomie
Differential Revision: https://phabricator.haskell.org/D921
GHC Trac Issues: #7170
-rw-r--r-- | rts/Schedule.c | 7 | ||||
-rw-r--r-- | rts/Weak.c | 11 | ||||
-rw-r--r-- | rts/sm/MarkWeak.c | 3 |
3 files changed, 17 insertions, 4 deletions
diff --git a/rts/Schedule.c b/rts/Schedule.c index 957aa4b9cb..f81fc0e703 100644 --- a/rts/Schedule.c +++ b/rts/Schedule.c @@ -251,7 +251,7 @@ schedule (Capability *initialCapability, Task *task) case SCHED_INTERRUPTING: debugTrace(DEBUG_sched, "SCHED_INTERRUPTING"); /* scheduleDoGC() deletes all the threads */ - scheduleDoGC(&cap,task,rtsFalse); + scheduleDoGC(&cap,task,rtsTrue); // after scheduleDoGC(), we must be shutting down. Either some // other Capability did the final GC, or we did it above, @@ -1458,6 +1458,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS, Capability *cap = *pcap; rtsBool heap_census; nat collect_gen; + rtsBool major_gc; #ifdef THREADED_RTS nat gc_type; nat i, sync; @@ -1476,6 +1477,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS, // Figure out which generation we are collecting, so that we can // decide whether this is a parallel GC or not. collect_gen = calcNeeded(force_major || heap_census, NULL); + major_gc = (collect_gen == RtsFlags.GcFlags.generations-1); #ifdef THREADED_RTS if (sched_state < SCHED_INTERRUPTING @@ -1618,8 +1620,9 @@ delete_threads_and_gc: * We now have all the capabilities; if we're in an interrupting * state, then we should take the opportunity to delete all the * threads in the system. + * Checking for major_gc ensures that the last GC is major. */ - if (sched_state == SCHED_INTERRUPTING) { + if (sched_state == SCHED_INTERRUPTING && major_gc) { deleteAllThreads(cap); #if defined(THREADED_RTS) // Discard all the sparks from every Capability. Why? diff --git a/rts/Weak.c b/rts/Weak.c index f8faa4e1f5..92f1bdbd02 100644 --- a/rts/Weak.c +++ b/rts/Weak.c @@ -43,7 +43,16 @@ runAllCFinalizers(StgWeak *list) } for (w = list; w; w = w->link) { - runCFinalizers((StgCFinalizerList *)w->cfinalizers); + // We need to filter out DEAD_WEAK objects, because it's not guaranteed + // that the list will not have them when shutting down. + // They only get filtered out during GC for the generation they + // belong to. + // If there's no major GC between the time that the finalizer for the + // object from the oldest generation is manually called and shutdown + // we end up running the same finalizer twice. See #7170. + if (w->header.info != &stg_DEAD_WEAK_info) { + runCFinalizers((StgCFinalizerList *)w->cfinalizers); + } } if (task != NULL) { diff --git a/rts/sm/MarkWeak.c b/rts/sm/MarkWeak.c index c5a107c480..60ac53f4b0 100644 --- a/rts/sm/MarkWeak.c +++ b/rts/sm/MarkWeak.c @@ -348,7 +348,8 @@ static void checkWeakPtrSanity(StgWeak *hd, StgWeak *tl) { StgWeak *w, *prev; for (w = hd; w != NULL; prev = w, w = w->link) { - ASSERT(INFO_PTR_TO_STRUCT(UNTAG_CLOSURE((StgClosure*)w)->header.info)->type == WEAK); + ASSERT(INFO_PTR_TO_STRUCT(UNTAG_CLOSURE((StgClosure*)w)->header.info)->type == WEAK + || UNTAG_CLOSURE((StgClosure*)w)->header.info == &stg_DEAD_WEAK_info); checkClosure((StgClosure*)w); } if (tl != NULL) { |