From 7de15753575022bf8d72a49537b2d77c81eb704b Mon Sep 17 00:00:00 2001 From: Mat Carter Date: Mon, 9 Mar 2026 08:38:37 -0700 Subject: [PATCH] Add StoreLoad barriers to ObjectMonitor::try_spin() for ARM64 Dekker protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Dekker protocol between try_spin() (ST _succ -> LD _owner) and exit() requires a StoreLoad barrier on both sides. The exit() side already has one (release_clear_owner + OrderAccess::storeload), but the spinner side was missing the corresponding fence. On ARM64, volatile store (STLR) followed by volatile load (LDAR) to different addresses does NOT imply StoreLoad ordering. Without the explicit barrier, the CPU can reorder the _owner load before the _succ store, causing the exiter to miss the successor designation while the spinner misses the lock release — leading to missed wakeups and thread starvation. Insert OrderAccess::storeload() after set_successor(current) in both places in try_spin(): before the spin loop and at the end of each iteration. --- src/hotspot/share/runtime/objectMonitor.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index cb69b82d7677e..3c98d15e4e9a8 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -2439,6 +2439,16 @@ bool ObjectMonitor::try_spin(JavaThread* current) { if (!has_successor()) { set_successor(current); } + // Dekker/Lamport duality with exit(): ST _succ; MEMBAR; LD _owner. + // The exiter's side (release_clear_owner + storeload) is in exit(). + // Here on the spinner's side, we need a StoreLoad barrier between + // setting _succ and reading _owner to prevent the CPU from reordering + // the _owner load before the _succ store. On ARM64 with MSVC + // /volatile:iso, Atomic::store/load are plain STR/LDR with no + // barrier, so without this fence the Dekker protocol is broken and + // the exiter may not see our successor designation while we may not + // see its lock release — leading to missed wakeups and starvation. + OrderAccess::storeload(); int64_t prv = NO_OWNER; // There are three ways to exit the following loop: @@ -2514,6 +2524,8 @@ bool ObjectMonitor::try_spin(JavaThread* current) { if (!has_successor()) { set_successor(current); + // See Dekker/storeload comment before the loop. + OrderAccess::storeload(); } }