Skip to content

Commit 3d8899b

Browse files
committed
[GR-44081] Use Blocker in Object.wait if pinned.
1 parent aeb670f commit 3d8899b

File tree

7 files changed

+67
-20
lines changed

7 files changed

+67
-20
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,6 @@ private int hashCodeSubst() {
107107
@Substitute
108108
@TargetElement(name = "wait")
109109
private void waitSubst(long timeoutMillis) throws InterruptedException {
110-
/*
111-
* JDK 19 and later: our monitor implementation does not pin virtual threads, so avoid
112-
* jdk.internal.misc.Blocker which expects and asserts that a virtual thread is pinned.
113-
* Also, we get interrupted on the virtual thread instead of the carrier thread, which
114-
* clears the carrier thread's interrupt status too, so we don't have to intercept an
115-
* InterruptedException from the carrier thread to clear the virtual thread interrupt.
116-
*/
117110
MonitorSupport.singleton().wait(this, timeoutMillis);
118111
}
119112

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,22 @@
4242

4343
import com.oracle.svm.core.Uninterruptible;
4444
import com.oracle.svm.core.WeakIdentityHashMap;
45+
import com.oracle.svm.core.annotate.Alias;
4546
import com.oracle.svm.core.annotate.TargetClass;
4647
import com.oracle.svm.core.heap.RestrictHeapAccess;
4748
import com.oracle.svm.core.heap.RestrictHeapAccess.Access;
4849
import com.oracle.svm.core.hub.DynamicHub;
4950
import com.oracle.svm.core.hub.DynamicHubCompanion;
5051
import com.oracle.svm.core.jdk.JDK17OrEarlier;
52+
import com.oracle.svm.core.jdk.JDK19OrLater;
5153
import com.oracle.svm.core.jfr.JfrTicks;
5254
import com.oracle.svm.core.jfr.events.JavaMonitorInflateEvent;
5355
import com.oracle.svm.core.monitor.JavaMonitorQueuedSynchronizer.JavaMonitorConditionObject;
5456
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
5557
import com.oracle.svm.core.stack.StackOverflowCheck;
5658
import com.oracle.svm.core.thread.ThreadStatus;
5759
import com.oracle.svm.core.thread.VMOperationControl;
60+
import com.oracle.svm.core.thread.VirtualThreads;
5861
import com.oracle.svm.core.util.VMError;
5962

6063
import jdk.internal.misc.Unsafe;
@@ -328,15 +331,35 @@ public boolean isLockedByAnyThread(Object obj) {
328331
@Override
329332
protected void doWait(Object obj, long timeoutMillis) throws InterruptedException {
330333
/*
331-
* Ensure that the current thread holds the lock. Required by the specification of
332-
* Object.wait, and also required for our implementation.
334+
* JDK 19 and later: our monitor implementation does not pin virtual threads, so avoid
335+
* jdk.internal.misc.Blocker which expects and asserts that a virtual thread is pinned
336+
* unless the thread is pinned for other reasons. Also, we get interrupted on the virtual
337+
* thread instead of the carrier thread, which clears the carrier thread's interrupt status
338+
* too, so we don't have to intercept an InterruptedException from the carrier thread to
339+
* clear the virtual thread interrupt.
333340
*/
334-
JavaMonitor lock = ensureLocked(obj, MonitorInflationCause.WAIT);
335-
JavaMonitorConditionObject condition = lock.getOrCreateCondition(true);
336-
if (timeoutMillis == 0L) {
337-
condition.await(obj);
338-
} else {
339-
condition.await(obj, timeoutMillis, TimeUnit.MILLISECONDS);
341+
long compensation = -1;
342+
boolean pinned = JavaVersionUtil.JAVA_SPEC >= 19 && VirtualThreads.isSupported() &&
343+
VirtualThreads.singleton().isVirtual(Thread.currentThread()) && VirtualThreads.singleton().isCurrentPinned();
344+
if (pinned) {
345+
compensation = Target_jdk_internal_misc_Blocker.begin();
346+
}
347+
try {
348+
/*
349+
* Ensure that the current thread holds the lock. Required by the specification of
350+
* Object.wait, and also required for our implementation.
351+
*/
352+
JavaMonitor lock = ensureLocked(obj, MonitorInflationCause.WAIT);
353+
JavaMonitorConditionObject condition = lock.getOrCreateCondition(true);
354+
if (timeoutMillis == 0L) {
355+
condition.await(obj);
356+
} else {
357+
condition.await(obj, timeoutMillis, TimeUnit.MILLISECONDS);
358+
}
359+
} finally {
360+
if (pinned) {
361+
Target_jdk_internal_misc_Blocker.end(compensation);
362+
}
340363
}
341364
}
342365

@@ -462,3 +485,12 @@ protected JavaMonitor newMonitorLock() {
462485
@TargetClass(value = ReferenceQueue.class, innerClass = "Lock", onlyWith = JDK17OrEarlier.class)
463486
final class Target_java_lang_ref_ReferenceQueue_Lock {
464487
}
488+
489+
@TargetClass(className = "jdk.internal.misc.Blocker", onlyWith = JDK19OrLater.class)
490+
final class Target_jdk_internal_misc_Blocker {
491+
@Alias
492+
public static native long begin();
493+
494+
@Alias
495+
public static native void end(long compensateReturn);
496+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/LoomVirtualThreads.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ public void unpinCurrent() {
131131
Target_jdk_internal_vm_Continuation.unpin();
132132
}
133133

134+
@Override
135+
public boolean isCurrentPinned() {
136+
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
137+
Target_jdk_internal_vm_ContinuationScope scope = carrier.cont.getScope();
138+
return Target_jdk_internal_vm_Continuation.isPinned(scope);
139+
}
140+
134141
@Override
135142
public Executor getScheduler(Thread thread) {
136143
return cast(thread).scheduler;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,7 @@ private void unmount() {
197197

198198
private boolean yieldContinuation() {
199199
assert this == Thread.currentThread();
200-
if (pins > 0) {
201-
return false;
202-
}
203-
JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor();
204-
if (anchor.isNonNull() && cont.getBaseSP().aboveThan(anchor.getLastJavaSP())) {
200+
if (isPinned()) {
205201
return false;
206202
}
207203

@@ -675,6 +671,15 @@ void unpin() {
675671
pins--;
676672
}
677673

674+
boolean isPinned() {
675+
assert currentThread() == this;
676+
if (pins > 0) {
677+
return true;
678+
}
679+
JavaFrameAnchor anchor = JavaFrameAnchors.getFrameAnchor();
680+
return anchor.isNonNull() && cont.getBaseSP().aboveThan(anchor.getLastJavaSP());
681+
}
682+
678683
@Override
679684
public void run() {
680685
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThreads.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ public void unpinCurrent() {
217217
current().unpin();
218218
}
219219

220+
@Override
221+
public boolean isCurrentPinned() {
222+
return current().isPinned();
223+
}
224+
220225
@Override
221226
public Executor getScheduler(Thread thread) {
222227
return cast(thread).getScheduler();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_jdk_internal_vm_Continuation.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,7 @@ static void unpin() {
203203
carrier.cont.pinCount--;
204204
}
205205
}
206+
207+
@Alias
208+
static native boolean isPinned(Target_jdk_internal_vm_ContinuationScope scope);
206209
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VirtualThreads.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ static boolean isSupported() {
7070

7171
void unpinCurrent();
7272

73+
boolean isCurrentPinned();
74+
7375
Executor getScheduler(Thread thread);
7476

7577
void blockedOn(Target_sun_nio_ch_Interruptible b);

0 commit comments

Comments
 (0)