3636import java .util .concurrent .atomic .AtomicInteger ;
3737import java .util .concurrent .atomic .AtomicLong ;
3838
39- import com .oracle .svm .core .SubstrateDiagnostics ;
4039import org .graalvm .compiler .api .replacements .Fold ;
4140import org .graalvm .compiler .core .common .SuppressFBWarnings ;
41+ import org .graalvm .compiler .serviceprovider .GraalUnsafeAccess ;
4242import org .graalvm .compiler .serviceprovider .JavaVersionUtil ;
4343import org .graalvm .nativeimage .CurrentIsolate ;
4444import org .graalvm .nativeimage .ImageSingletons ;
5454import org .graalvm .word .PointerBase ;
5555import org .graalvm .word .WordFactory ;
5656
57+ import com .oracle .svm .core .SubstrateDiagnostics ;
5758import com .oracle .svm .core .SubstrateOptions ;
5859import com .oracle .svm .core .SubstrateUtil ;
5960import com .oracle .svm .core .annotate .NeverInline ;
6364import com .oracle .svm .core .heap .ReferenceHandlerThreadSupport ;
6465import com .oracle .svm .core .jdk .StackTraceUtils ;
6566import com .oracle .svm .core .jdk .UninterruptibleUtils ;
66- import com .oracle .svm .core .jdk .UninterruptibleUtils .AtomicReference ;
6767import com .oracle .svm .core .locks .VMMutex ;
6868import com .oracle .svm .core .log .Log ;
6969import com .oracle .svm .core .monitor .MonitorSupport ;
7878import com .oracle .svm .core .util .TimeUtils ;
7979import 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