Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/java.base/share/classes/java/lang/VirtualThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,13 @@ private void afterYield() {
setState(newState = TIMED_PARKED);
}

// Full fence (StoreLoad) to ensure the PARKED/TIMED_PARKED state
// is visible before reading parkPermit (Dekker pattern with
// unpark which writes parkPermit then reads state).
// Note: storeFence is insufficient — on ARM64 it only emits
// LoadStore+StoreStore (dmb ishst), not StoreLoad (dmb ish).
U.fullFence();

// may have been unparked while parking
if (parkPermit && compareAndSetState(newState, UNPARKED)) {
// lazy submit if local queue is empty
Expand All @@ -604,6 +611,10 @@ private void afterYield() {
if (s == BLOCKING) {
setState(BLOCKED);

// Full fence (StoreLoad) for Dekker pattern with unblock
// which writes blockPermit then reads state.
U.fullFence();

// may have been unblocked while blocking
if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) {
// lazy submit if local queue is empty
Expand All @@ -619,6 +630,9 @@ private void afterYield() {
boolean interruptible = interruptibleWait;
if (s == WAITING) {
setState(newState = WAIT);
// Full fence (StoreLoad) for Dekker pattern with notify
// which writes notified then reads state.
U.fullFence();
// may have been notified while in transition
blocked = notified && compareAndSetState(WAIT, BLOCKED);
} else {
Expand All @@ -635,6 +649,9 @@ private void afterYield() {
byte seqNo = ++timedWaitSeqNo;
timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS);
setState(newState = TIMED_WAIT);
// Full fence (StoreLoad) for Dekker pattern with notify
// which writes notified then reads state.
U.fullFence();
// May have been notified while in transition. This must be done while
// holding the monitor to avoid changing the state of a new timed wait call.
blocked = notified && compareAndSetState(TIMED_WAIT, BLOCKED);
Expand Down Expand Up @@ -675,6 +692,15 @@ private void afterDone(boolean notifyContainer) {
assert carrierThread == null;
setState(TERMINATED);

// Full fence (StoreLoad) to ensure the TERMINATED state is
// visible before reading notifyAllAfterTerminate (Dekker pattern
// with beforeJoin which writes notifyAllAfterTerminate then
// reads state). Without this, on ARM64 the volatile write of
// state and the subsequent volatile read can be reordered,
// causing a missed-wakeup where both sides miss each other's
// store.
U.fullFence();

// notifyAll to wakeup any threads waiting for this thread to terminate
if (notifyAllAfterTerminate) {
synchronized (this) {
Expand Down Expand Up @@ -870,6 +896,10 @@ private void parkOnCarrierThread(boolean timed, long nanos) {
*/
private void unpark(boolean lazySubmit) {
if (!getAndSetParkPermit(true) && currentThread() != this) {
// Full fence (StoreLoad) to ensure parkPermit=true is visible
// before reading state (Dekker pattern with afterYield PARKING
// path which writes state then reads parkPermit).
U.fullFence();
int s = state();

// unparked while parked
Expand Down Expand Up @@ -912,6 +942,7 @@ void unpark() {
private void unblock() {
assert !Thread.currentThread().isVirtual();
blockPermit = true;
U.fullFence(); // Full fence (StoreLoad) for Dekker with afterYield BLOCKING path
if (state() == BLOCKED && compareAndSetState(BLOCKED, UNBLOCKED)) {
submitRunContinuation();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,11 @@ final Object xfer(Object e, long ns) {
q = p.next;
if (p.isData != haveData && haveData != (m != null)) {
if (p.cmpExItem(m, e) == m) {
// Full fence (StoreLoad) for Dekker with await() which
// writes waiter then reads item. On ARM64, CAS
// (ldaxr/stlxr) + plain load to a different field does
// NOT provide StoreLoad ordering.
VarHandle.fullFence();
Thread w = p.waiter; // matched complementary node
if (p != h && h == cmpExHead(h, (q == null) ? p : q))
h.next = h; // advance head; self-link old
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ final Object xferLifo(Object e, long ns) {
else if (p.cmpExItem(m, e) != m)
p = head; // missed; restart
else { // matched complementary node
// Full fence (StoreLoad) for Dekker with await() which
// writes waiter then reads item. On ARM64, CAS
// (ldaxr/stlxr) + plain load to a different field does
// NOT provide StoreLoad ordering.
VarHandle.fullFence();
Thread w = p.waiter;
cmpExHead(p, p.next);
LockSupport.unpark(w);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,13 @@ final int acquire(Node node, int arg, boolean shared,
Thread.onSpinWait();
} else if (node.status == 0) {
node.status = WAITING; // enable signal and recheck
// Full fence (StoreLoad) to ensure WAITING status is visible
// before re-reading state in tryAcquire/tryAcquireShared
// (Dekker pattern with releaseShared/release which writes
// state then reads node.status in signalNext).
// On ARM64, volatile write (stlr) + volatile read (ldar) to
// different addresses does NOT provide StoreLoad ordering.
U.fullFence();
} else {
spins = postSpins = (byte)((postSpins << 1) | 1);
try {
Expand Down Expand Up @@ -1097,6 +1104,13 @@ public final boolean tryAcquireNanos(int arg, long nanosTimeout)
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
// Full fence (StoreLoad) to ensure the state update from
// tryRelease is visible before reading node.status in signalNext
// (Dekker pattern: release writes state then reads status,
// acquire writes status then reads state).
// On ARM64, CAS (stlxr/release) + ldar to different addresses
// does NOT provide StoreLoad ordering.
U.fullFence();
signalNext(head);
return true;
}
Expand Down Expand Up @@ -1184,6 +1198,8 @@ public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
// Full fence (StoreLoad) — see comment in release()
U.fullFence();
signalNext(head);
return true;
}
Expand Down
Loading