4242import com .oracle .svm .core .annotate .TargetClass ;
4343import com .oracle .svm .core .jdk .ContinuationsSupported ;
4444import com .oracle .svm .core .jdk .NotLoomJDK ;
45+ import com .oracle .svm .core .monitor .MonitorSupport ;
4546import com .oracle .svm .core .stack .JavaFrameAnchor ;
4647import com .oracle .svm .core .stack .JavaFrameAnchors ;
4748import com .oracle .svm .core .util .VMError ;
@@ -168,10 +169,13 @@ private void mount() {
168169 if (JavaThreads .isInterrupted (this )) {
169170 JavaThreads .platformSetInterrupt (carrier );
170171 } else if (JavaThreads .isInterrupted (carrier )) {
171- synchronized (interruptLock ()) {
172+ Object token = switchToCarrierAndAcquireInterruptLock ();
173+ try {
172174 if (!JavaThreads .isInterrupted (this )) {
173175 JavaThreads .platformGetAndClearInterrupt (carrier );
174176 }
177+ } finally {
178+ releaseInterruptLockAndSwitchBack (token );
175179 }
176180 }
177181
@@ -374,11 +378,14 @@ void unpark() {
374378 submitRunContinuation ();
375379 }
376380 } else if (s == PINNED ) {
377- synchronized (interruptLock ()) {
381+ Object token = switchToCarrierAndAcquireInterruptLock ();
382+ try {
378383 Thread carrier = carrierThread ;
379384 if (carrier != null && state () == PINNED ) {
380385 U .unpark (carrier );
381386 }
387+ } finally {
388+ releaseInterruptLockAndSwitchBack (token );
382389 }
383390 }
384391 }
@@ -428,10 +435,41 @@ private Object interruptLock() {
428435 return JavaThreads .toTarget (this ).blockerLock ;
429436 }
430437
438+ /** @see #releaseInterruptLockAndSwitchBack */
439+ private Object switchToCarrierAndAcquireInterruptLock () {
440+ Object token = null ;
441+ Thread current = Thread .currentThread ();
442+ if (current instanceof SubstrateVirtualThread ) {
443+ SubstrateVirtualThread vthread = (SubstrateVirtualThread ) current ;
444+ /*
445+ * If we block on the interrupt lock in the virtual thread, we yield, for which we first
446+ * unmount. Unmounting also tries to acquire the interrupt lock and we might block
447+ * again, this time on the carrier thread. Then, the virtual thread cannot continue to
448+ * yield and execute on another thread later, and the carrier thread might not get
449+ * unparked, and both threads are stuck.
450+ */
451+ Thread carrier = vthread .carrierThread ;
452+ JavaThreads .setCurrentThread (carrier , carrier );
453+ token = vthread ;
454+ }
455+ MonitorSupport .singleton ().monitorEnter (interruptLock ());
456+ return token ;
457+ }
458+
459+ /** @see #switchToCarrierAndAcquireInterruptLock */
460+ private void releaseInterruptLockAndSwitchBack (Object token ) {
461+ MonitorSupport .singleton ().monitorExit (interruptLock ());
462+ if (token != null ) {
463+ SubstrateVirtualThread vthread = (SubstrateVirtualThread ) token ;
464+ JavaThreads .setCurrentThread (vthread .carrierThread , vthread );
465+ }
466+ }
467+
431468 @ Override
432469 public void interrupt () {
433470 if (Thread .currentThread () != this ) {
434- synchronized (interruptLock ()) {
471+ Object token = switchToCarrierAndAcquireInterruptLock ();
472+ try {
435473 JavaThreads .getAndWriteInterruptedFlag (this , true );
436474 Target_sun_nio_ch_Interruptible b = JavaThreads .toTarget (this ).blocker ;
437475 if (b != null ) {
@@ -441,6 +479,8 @@ public void interrupt() {
441479 if (carrier != null ) {
442480 JavaThreads .platformSetInterrupt (carrier );
443481 }
482+ } finally {
483+ releaseInterruptLockAndSwitchBack (token );
444484 }
445485 } else {
446486 JavaThreads .getAndWriteInterruptedFlag (this , true );
@@ -451,8 +491,11 @@ public void interrupt() {
451491
452492 boolean getAndClearCarrierInterrupt () {
453493 assert Thread .currentThread () == this ;
454- synchronized (interruptLock ()) {
494+ Object token = switchToCarrierAndAcquireInterruptLock ();
495+ try {
455496 return JavaThreads .platformGetAndClearInterrupt (carrierThread );
497+ } finally {
498+ releaseInterruptLockAndSwitchBack (token );
456499 }
457500 }
458501
@@ -496,12 +539,15 @@ public Thread.State getState() {
496539 return Thread .State .RUNNABLE ;
497540 case RUNNING :
498541 // if mounted then return state of carrier thread
499- synchronized (interruptLock ()) {
542+ Object token = switchToCarrierAndAcquireInterruptLock ();
543+ try {
500544 @ SuppressWarnings ("hiding" )
501545 Thread carrierThread = this .carrierThread ;
502546 if (carrierThread != null ) {
503547 return JavaThreads .getThreadState (carrierThread );
504548 }
549+ } finally {
550+ releaseInterruptLockAndSwitchBack (token );
505551 }
506552 // runnable, mounted
507553 return Thread .State .RUNNABLE ;
@@ -533,14 +579,17 @@ public String toString() {
533579 Thread carrier = carrierThread ;
534580 if (carrier != null ) {
535581 // include the carrier thread state and name when mounted
536- synchronized (interruptLock ()) {
582+ Object token = switchToCarrierAndAcquireInterruptLock ();
583+ try {
537584 carrier = carrierThread ;
538585 if (carrier != null ) {
539586 String stateAsString = JavaThreads .getThreadState (carrier ).toString ();
540587 sb .append (stateAsString .toLowerCase (Locale .ROOT ));
541588 sb .append ('@' );
542589 sb .append (carrier .getName ());
543590 }
591+ } finally {
592+ releaseInterruptLockAndSwitchBack (token );
544593 }
545594 }
546595 // include virtual thread state when not mounted
0 commit comments