Skip to content

Commit fea842c

Browse files
[GR-35557] Fix transient issues with ParkEvents.
PullRequest: graal/10497
2 parents 229aea5 + d8ccd86 commit fea842c

File tree

10 files changed

+419
-168
lines changed

10 files changed

+419
-168
lines changed

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixVMThreads.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@ public void yield() {
8383
Sched.NoTransitions.sched_yield();
8484
}
8585

86+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
8687
@Override
87-
public boolean supportsPatientSafepoints() {
88+
public boolean supportsNativeYieldAndSleep() {
8889
return true;
8990
}
9091

substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVMThreads.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ public void yield() {
8686
Process.NoTransitions.SwitchToThread();
8787
}
8888

89+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
8990
@Override
90-
public boolean supportsPatientSafepoints() {
91+
public boolean supportsNativeYieldAndSleep() {
9192
return true;
9293
}
9394

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ private static void logDeoptSourceFrameOperation(Pointer sp, DeoptimizedFrame de
751751
};
752752

753753
public static void logRecentDeoptimizationEvents(Log log) {
754-
log.string("Recent deoptimization events:").indent(true);
754+
log.string("Recent deoptimization events (oldest first):").indent(true);
755755
recentDeoptimizationEvents.foreach(log, deoptEventsConsumer);
756756
log.indent(false);
757757
}

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

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636
import java.util.concurrent.atomic.AtomicInteger;
3737
import java.util.concurrent.atomic.AtomicLong;
3838

39-
import com.oracle.svm.core.SubstrateDiagnostics;
4039
import org.graalvm.compiler.api.replacements.Fold;
4140
import org.graalvm.compiler.core.common.SuppressFBWarnings;
41+
import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess;
4242
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
4343
import org.graalvm.nativeimage.CurrentIsolate;
4444
import org.graalvm.nativeimage.ImageSingletons;
@@ -54,6 +54,7 @@
5454
import org.graalvm.word.PointerBase;
5555
import org.graalvm.word.WordFactory;
5656

57+
import com.oracle.svm.core.SubstrateDiagnostics;
5758
import com.oracle.svm.core.SubstrateOptions;
5859
import com.oracle.svm.core.SubstrateUtil;
5960
import com.oracle.svm.core.annotate.NeverInline;
@@ -63,7 +64,6 @@
6364
import com.oracle.svm.core.heap.ReferenceHandlerThreadSupport;
6465
import com.oracle.svm.core.jdk.StackTraceUtils;
6566
import com.oracle.svm.core.jdk.UninterruptibleUtils;
66-
import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicReference;
6767
import com.oracle.svm.core.locks.VMMutex;
6868
import com.oracle.svm.core.log.Log;
6969
import com.oracle.svm.core.monitor.MonitorSupport;
@@ -78,13 +78,18 @@
7878
import com.oracle.svm.core.util.TimeUtils;
7979
import com.oracle.svm.core.util.VMError;
8080

81-
public abstract class JavaThreads {
81+
// Checkstyle: stop
82+
import sun.misc.Unsafe;
83+
// Checkstyle: resume
8284

85+
public abstract class JavaThreads {
8386
@Fold
8487
public static JavaThreads singleton() {
8588
return ImageSingletons.lookup(JavaThreads.class);
8689
}
8790

91+
private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe();
92+
8893
/** The {@link java.lang.Thread} for the {@link IsolateThread}. */
8994
static final FastThreadLocalObject<Thread> currentThread = FastThreadLocalFactory.createObject(Thread.class, "JavaThreads.currentThread").setMaxOffset(FastThreadLocal.BYTE_OFFSET);
9095

@@ -183,16 +188,6 @@ public static boolean isInterrupted(Thread thread) {
183188
return toTarget(thread).interruptedJDK11OrEarlier;
184189
}
185190

186-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
187-
protected static AtomicReference<ParkEvent> getUnsafeParkEvent(Thread thread) {
188-
return toTarget(thread).unsafeParkEvent;
189-
}
190-
191-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
192-
protected static AtomicReference<ParkEvent> getSleepParkEvent(Thread thread) {
193-
return toTarget(thread).sleepParkEvent;
194-
}
195-
196191
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
197192
protected static boolean wasStartedByCurrentIsolate(IsolateThread thread) {
198193
Thread javaThread = currentThread.get(thread);
@@ -430,8 +425,7 @@ public static void detachThread(IsolateThread vmThread) {
430425
VMThreads.THREAD_MUTEX.assertIsOwner("Must hold the THREAD_MUTEX.");
431426

432427
Thread thread = currentThread.get(vmThread);
433-
ParkEvent.detach(getUnsafeParkEvent(thread));
434-
ParkEvent.detach(getSleepParkEvent(thread));
428+
toTarget(thread).threadData.detach();
435429
toTarget(thread).isolateThread = WordFactory.nullPointer();
436430
if (!thread.isDaemon()) {
437431
nonDaemonThreads.decrementAndGet();
@@ -747,20 +741,21 @@ static void initializeNewThread(
747741
/** Interruptibly park the current thread. */
748742
static void park() {
749743
VMOperationControl.guaranteeOkayToBlock("[JavaThreads.park(): Should not park when it is not okay to block.]");
750-
final Thread thread = Thread.currentThread();
744+
Thread thread = Thread.currentThread();
751745
if (isInterrupted(thread)) { // avoid state changes and synchronization
752746
return;
753747
}
754-
/*
755-
* We can defer assigning a ParkEvent to here because Thread.interrupt() is guaranteed to
756-
* assign and unpark one if it doesn't yet exist, otherwise we could lose a wakeup.
757-
*/
758-
final ParkEvent parkEvent = ensureUnsafeParkEvent(thread);
748+
749+
ParkEvent parkEvent = getCurrentThreadData().ensureUnsafeParkEvent();
759750
// Change the Java thread state while parking.
760-
final int oldStatus = JavaThreads.getThreadStatus(thread);
751+
int oldStatus = JavaThreads.getThreadStatus(thread);
761752
int newStatus = MonitorSupport.singleton().maybeAdjustNewParkStatus(ThreadStatus.PARKED);
762753
JavaThreads.setThreadStatus(thread, newStatus);
763754
try {
755+
/*
756+
* If another thread interrupted this thread in the meanwhile, then the call below won't
757+
* block because Thread.interrupt() modifies the ParkEvent.
758+
*/
764759
parkEvent.condWait();
765760
} finally {
766761
JavaThreads.setThreadStatus(thread, oldStatus);
@@ -770,19 +765,20 @@ static void park() {
770765
/** Interruptibly park the current thread for the given number of nanoseconds. */
771766
static void park(long delayNanos) {
772767
VMOperationControl.guaranteeOkayToBlock("[JavaThreads.park(long): Should not park when it is not okay to block.]");
773-
final Thread thread = Thread.currentThread();
768+
Thread thread = Thread.currentThread();
774769
if (isInterrupted(thread)) { // avoid state changes and synchronization
775770
return;
776771
}
777-
/*
778-
* We can defer assigning a ParkEvent to here because Thread.interrupt() is guaranteed to
779-
* assign and unpark one if it doesn't yet exist, otherwise we could lose a wakeup.
780-
*/
781-
final ParkEvent parkEvent = ensureUnsafeParkEvent(thread);
782-
final int oldStatus = JavaThreads.getThreadStatus(thread);
772+
773+
ParkEvent parkEvent = getCurrentThreadData().ensureUnsafeParkEvent();
774+
int oldStatus = JavaThreads.getThreadStatus(thread);
783775
int newStatus = MonitorSupport.singleton().maybeAdjustNewParkStatus(ThreadStatus.PARKED_TIMED);
784776
JavaThreads.setThreadStatus(thread, newStatus);
785777
try {
778+
/*
779+
* If another thread interrupted this thread in the meanwhile, then the call below won't
780+
* block because Thread.interrupt() modifies the ParkEvent.
781+
*/
786782
parkEvent.condTimedWait(delayNanos);
787783
} finally {
788784
JavaThreads.setThreadStatus(thread, oldStatus);
@@ -796,12 +792,14 @@ static void park(long delayNanos) {
796792
* @see #park(long)
797793
*/
798794
static void unpark(Thread thread) {
799-
ensureUnsafeParkEvent(thread).unpark();
800-
}
801-
802-
/** Get the Park event for a thread, initializing it if necessary. */
803-
private static ParkEvent ensureUnsafeParkEvent(Thread thread) {
804-
return ParkEvent.initializeOnce(JavaThreads.getUnsafeParkEvent(thread), false);
795+
ThreadData threadData = acquireThreadData(thread);
796+
if (threadData != null) {
797+
try {
798+
threadData.ensureUnsafeParkEvent().unpark();
799+
} finally {
800+
threadData.release();
801+
}
802+
}
805803
}
806804

807805
/** Sleep for the given number of nanoseconds, dealing with early wakeups and interruptions. */
@@ -817,23 +815,31 @@ static void sleep(long millis) throws InterruptedException {
817815

818816
static void sleep0(long delayNanos) {
819817
VMOperationControl.guaranteeOkayToBlock("[JavaThreads.sleep(long): Should not sleep when it is not okay to block.]");
820-
final Thread thread = Thread.currentThread();
821-
final ParkEvent sleepEvent = ParkEvent.initializeOnce(JavaThreads.getSleepParkEvent(thread), true);
818+
Thread thread = Thread.currentThread();
819+
ParkEvent sleepEvent = getCurrentThreadData().ensureSleepParkEvent();
822820
sleepEvent.reset();
821+
823822
/*
824823
* It is critical to reset the event *before* checking for an interrupt to avoid losing a
825824
* wakeup in the race. This requires that updates to the event's unparked status and updates
826-
* to the thread's interrupt status cannot be reordered with regard to each other. Another
827-
* important aspect is that the thread must have a sleepParkEvent assigned to it *before*
828-
* the interrupted check because if not, the interrupt code will not assign one and the
829-
* wakeup will be lost, too.
825+
* to the thread's interrupt status cannot be reordered with regard to each other.
826+
*
827+
* Another important aspect is that the thread must have a sleepParkEvent assigned to it
828+
* *before* the interrupted check because if not, the interrupt code will not assign one and
829+
* the wakeup will be lost.
830830
*/
831+
UNSAFE.fullFence();
832+
831833
if (isInterrupted(thread)) {
832834
return; // likely leaves a stale unpark which will be reset before the next sleep()
833835
}
834836
final int oldStatus = JavaThreads.getThreadStatus(thread);
835837
JavaThreads.setThreadStatus(thread, ThreadStatus.SLEEPING);
836838
try {
839+
/*
840+
* If another thread interrupted this thread in the meanwhile, then the call below won't
841+
* block because Thread.interrupt() modifies the ParkEvent.
842+
*/
837843
sleepEvent.condTimedWait(delayNanos);
838844
} finally {
839845
JavaThreads.setThreadStatus(thread, oldStatus);
@@ -846,16 +852,32 @@ static void sleep0(long delayNanos) {
846852
* @see #sleep(long)
847853
*/
848854
static void interrupt(Thread thread) {
849-
final ParkEvent sleepEvent = JavaThreads.getSleepParkEvent(thread).get();
850-
if (sleepEvent != null) {
851-
sleepEvent.unpark();
855+
ThreadData threadData = acquireThreadData(thread);
856+
if (threadData != null) {
857+
try {
858+
ParkEvent sleepEvent = threadData.getSleepParkEvent();
859+
if (sleepEvent != null) {
860+
sleepEvent.unpark();
861+
}
862+
} finally {
863+
threadData.release();
864+
}
852865
}
853866
}
854867

855868
static boolean isAlive(int threadStatus) {
856869
return !(threadStatus == ThreadStatus.NEW || threadStatus == ThreadStatus.TERMINATED);
857870
}
858871

872+
private static ThreadData acquireThreadData(Thread thread) {
873+
return toTarget(thread).threadData.acquire();
874+
}
875+
876+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
877+
static ThreadData getCurrentThreadData() {
878+
return (ThreadData) toTarget(Thread.currentThread()).threadData;
879+
}
880+
859881
/**
860882
* Builds a list of all application threads. This must be done in a VM operation because only
861883
* there we are allowed to allocate Java memory while holding the {@link VMThreads#THREAD_MUTEX}

0 commit comments

Comments
 (0)