From c61783d8169072a9f668d3b568dc317ee35333ed Mon Sep 17 00:00:00 2001 From: "Enzo Persillet (Tutez)" Date: Tue, 29 Sep 2026 22:42:37 +0200 Subject: [PATCH] Keep the mark chunk pop locks exclusive. compare_exchange_strong writes the current value into expected when it fails. The spin loops kept that value, so the next attempt compared against 1 and succeeded while another thread still held the lock. Two marking threads could then pop the same chunk list at once, and a chunk could be lost: its objects were marked but never traversed, and what only they referenced was freed. Co-Authored-By: Claude Opus 5.5 --- src/hx/gc/Immix.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 9eb91603e..dab8b769b 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -1655,6 +1655,9 @@ struct GlobalChunks int expected{ 0 }; while(false == processListPopLock.compare_exchange_strong(expected, 1)) { + // A failed exchange stores the current value in expected; without this the next + // attempt would compare against 1 and take a lock another thread still holds. + expected = 0; // Spin #ifdef PROFILE_THREAD_USAGE sSpinCount++; @@ -1739,6 +1742,9 @@ struct GlobalChunks int expected{ 0 }; while(false == freeListPopLock.compare_exchange_strong(expected, 1)) { + // A failed exchange stores the current value in expected; without this the next + // attempt would compare against 1 and take a lock another thread still holds. + expected = 0; // Spin #ifdef PROFILE_THREAD_USAGE sSpinCount++;