Skip to content

Commit e2a26aa

Browse files
authored
use atomics in the write barrier slow path (#52463)
Use atomics in the write-barrier slow-path to prevent duplicates in the remset. As discussed in #50419, setting the mark bit is idempotent, but updating page metadata in the mark phase is not.
1 parent 84cfe04 commit e2a26aa

File tree

1 file changed

+11
-7
lines changed

1 file changed

+11
-7
lines changed

src/gc.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,13 +1788,17 @@ JL_DLLEXPORT void jl_gc_queue_root(const jl_value_t *ptr)
17881788
{
17891789
jl_ptls_t ptls = jl_current_task->ptls;
17901790
jl_taggedvalue_t *o = jl_astaggedvalue(ptr);
1791-
// The modification of the `gc_bits` is not atomic but it
1792-
// should be safe here since GC is not allowed to run here and we only
1793-
// write GC_OLD to the GC bits outside GC. This could cause
1794-
// duplicated objects in the remset but that shouldn't be a problem.
1795-
o->bits.gc = GC_MARKED;
1796-
arraylist_push(ptls->heap.remset, (jl_value_t*)ptr);
1797-
ptls->heap.remset_nptr++; // conservative
1791+
// The modification of the `gc_bits` needs to be atomic.
1792+
// We need to ensure that objects are in the remset at
1793+
// most once, since the mark phase may update page metadata,
1794+
// which is not idempotent. See comments in https://github.com/JuliaLang/julia/issues/50419
1795+
uintptr_t header = jl_atomic_load_relaxed((_Atomic(uintptr_t) *)&o->header);
1796+
header &= ~GC_OLD; // clear the age bit
1797+
header = jl_atomic_exchange_relaxed((_Atomic(uintptr_t) *)&o->header, header);
1798+
if (header & GC_OLD) { // write barrier has not been triggered in this object yet
1799+
arraylist_push(ptls->heap.remset, (jl_value_t*)ptr);
1800+
ptls->heap.remset_nptr++; // conservative
1801+
}
17981802
}
17991803

18001804
void jl_gc_queue_multiroot(const jl_value_t *parent, const void *ptr, jl_datatype_t *dt) JL_NOTSAFEPOINT

0 commit comments

Comments
 (0)