summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGHC GitLab CI <ghc-ci@gitlab-haskell.org>2020-11-26 01:11:35 +0000
committerMarge Bot <ben+marge-bot@smart-cactus.org>2020-11-29 15:33:54 -0500
commit3aa603620ef5a6aae1778278aa9914f344ab526e (patch)
tree69c6eca57f7c2479c42e32f1f2b73a3a9b0c1e9b
parent60d088aba238c7265adf76840ec1d883373b0e20 (diff)
downloadhaskell-3aa603620ef5a6aae1778278aa9914f344ab526e.tar.gz
nonmoving: Ensure that evacuated large objects are marked
See Note [Non-moving GC: Marking evacuated objects].
-rw-r--r--rts/sm/Evac.c61
-rw-r--r--rts/sm/NonMoving.c6
2 files changed, 60 insertions, 7 deletions
diff --git a/rts/sm/Evac.c b/rts/sm/Evac.c
index 8595a80c38..e9b9c84324 100644
--- a/rts/sm/Evac.c
+++ b/rts/sm/Evac.c
@@ -109,6 +109,8 @@ alloc_for_copy (uint32_t size, uint32_t gen_no)
//
// However, if we are in a deadlock detection GC then we disable aging
// so there is no need.
+ //
+ // See Note [Non-moving GC: Marking evacuated objects].
if (major_gc && !deadlock_detect_gc)
markQueuePushClosureGC(&gct->cap->upd_rem_set.queue, (StgClosure *) to);
return to;
@@ -134,6 +136,52 @@ alloc_for_copy (uint32_t size, uint32_t gen_no)
The evacuate() code
-------------------------------------------------------------------------- */
+/*
+ * Note [Non-moving GC: Marking evacuated objects]
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * When the non-moving collector is in use we must be careful to ensure that any
+ * references to objects in the non-moving generation from younger generations
+ * are pushed to the mark queue.
+ *
+ * In particular we need to ensure that we handle newly-promoted objects are
+ * correctly marked. For instance, consider this case:
+ *
+ * generation 0 generation 1
+ * ────────────── ──────────────
+ *
+ * ┌───────┐
+ * ┌───────┐ │ A │
+ * │ B │ ◁────────────────────────── │ │
+ * │ │ ──┬─────────────────┐ └───────┘
+ * └───────┘ ┆ after GC │
+ * ┆ │
+ * ┌───────┐ ┆ before GC │ ┌───────┐
+ * │ C │ ◁┄┘ └─────▷ │ C' │
+ * │ │ │ │
+ * └───────┘ └───────┘
+ *
+ *
+ * In this case object C started off in generation 0 and was evacuated into
+ * generation 1 during the preparatory GC. However, the only reference to C'
+ * is from B, which lives in the generation 0 (via aging); this reference will
+ * not be visible to the concurrent non-moving collector (which can only
+ * traverse the generation 1 heap). Consequently, upon evacuating C we need to
+ * ensure that C' is added to the update remembered set as we know that it will
+ * continue to be reachable via B (which is assumed to be reachable as it lives
+ * in a younger generation).
+ *
+ * Where this happens depends upon the type of the object (e.g. C'):
+ *
+ * - In the case of "normal" small heap-allocated objects this happens in
+ * alloc_for_copy.
+ * - In the case of compact region this happens in evacuate_compact.
+ * - In the case of large objects this happens in evacuate_large.
+ *
+ * See also Note [Aging under the non-moving collector] in NonMoving.c.
+ *
+ */
+
/* size is in words
We want to *always* inline this as often the size of the closure is static,
@@ -356,6 +404,9 @@ evacuate_large(StgPtr p)
__atomic_fetch_or(&bd->flags, BF_EVACUATED, __ATOMIC_ACQ_REL);
if (RTS_UNLIKELY(RtsFlags.GcFlags.useNonmoving && new_gen == oldest_gen)) {
__atomic_fetch_or(&bd->flags, BF_NONMOVING, __ATOMIC_ACQ_REL);
+
+ // See Note [Non-moving GC: Marking evacuated objects].
+ markQueuePushClosureGC(&gct->cap->upd_rem_set.queue, (StgClosure *) p);
}
initBdescr(bd, new_gen, new_gen->to);
@@ -510,6 +561,9 @@ evacuate_compact (StgPtr p)
bd->flags |= BF_EVACUATED;
if (RTS_UNLIKELY(RtsFlags.GcFlags.useNonmoving && new_gen == oldest_gen)) {
__atomic_fetch_or(&bd->flags, BF_NONMOVING, __ATOMIC_RELAXED);
+
+ // See Note [Non-moving GC: Marking evacuated objects].
+ markQueuePushClosureGC(&gct->cap->upd_rem_set.queue, (StgClosure *) str);
}
initBdescr(bd, new_gen, new_gen->to);
@@ -695,13 +749,6 @@ loop:
*/
if (flags & BF_LARGE) {
evacuate_large((P_)q);
-
- // We may have evacuated the block to the nonmoving generation. If so
- // we need to make sure it is added to the mark queue since the only
- // reference to it may be from the moving heap.
- if (major_gc && flags & BF_NONMOVING && !deadlock_detect_gc) {
- markQueuePushClosureGC(&gct->cap->upd_rem_set.queue, q);
- }
return;
}
diff --git a/rts/sm/NonMoving.c b/rts/sm/NonMoving.c
index 182bdadbad..b92bf2276e 100644
--- a/rts/sm/NonMoving.c
+++ b/rts/sm/NonMoving.c
@@ -204,6 +204,10 @@ Mutex concurrent_coll_finished_lock;
* - Note [Aging under the non-moving collector] (NonMoving.c) describes how
* we accommodate aging
*
+ * - Note [Non-moving GC: Marking evacuated objects] (Evac.c) describes how
+ * non-moving objects reached by evacuate() are marked, which is necessary
+ * due to aging.
+ *
* - Note [Large objects in the non-moving collector] (NonMovingMark.c)
* describes how we track large objects.
*
@@ -317,6 +321,8 @@ Mutex concurrent_coll_finished_lock;
*
* The non-moving collector will come to C in the mark queue and mark it.
*
+ * The implementation details of this are described in Note [Non-moving GC:
+ * Marking evacuated objects] in Evac.c.
*
* Note [Deadlock detection under the non-moving collector]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~