36
36
import java .util .concurrent .atomic .AtomicInteger ;
37
37
import java .util .concurrent .atomic .AtomicLong ;
38
38
39
- import com .oracle .svm .core .SubstrateDiagnostics ;
40
39
import org .graalvm .compiler .api .replacements .Fold ;
41
40
import org .graalvm .compiler .core .common .SuppressFBWarnings ;
41
+ import org .graalvm .compiler .serviceprovider .GraalUnsafeAccess ;
42
42
import org .graalvm .compiler .serviceprovider .JavaVersionUtil ;
43
43
import org .graalvm .nativeimage .CurrentIsolate ;
44
44
import org .graalvm .nativeimage .ImageSingletons ;
54
54
import org .graalvm .word .PointerBase ;
55
55
import org .graalvm .word .WordFactory ;
56
56
57
+ import com .oracle .svm .core .SubstrateDiagnostics ;
57
58
import com .oracle .svm .core .SubstrateOptions ;
58
59
import com .oracle .svm .core .SubstrateUtil ;
59
60
import com .oracle .svm .core .annotate .NeverInline ;
63
64
import com .oracle .svm .core .heap .ReferenceHandlerThreadSupport ;
64
65
import com .oracle .svm .core .jdk .StackTraceUtils ;
65
66
import com .oracle .svm .core .jdk .UninterruptibleUtils ;
66
- import com .oracle .svm .core .jdk .UninterruptibleUtils .AtomicReference ;
67
67
import com .oracle .svm .core .locks .VMMutex ;
68
68
import com .oracle .svm .core .log .Log ;
69
69
import com .oracle .svm .core .monitor .MonitorSupport ;
78
78
import com .oracle .svm .core .util .TimeUtils ;
79
79
import com .oracle .svm .core .util .VMError ;
80
80
81
- public abstract class JavaThreads {
81
+ // Checkstyle: stop
82
+ import sun .misc .Unsafe ;
83
+ // Checkstyle: resume
82
84
85
+ public abstract class JavaThreads {
83
86
@ Fold
84
87
public static JavaThreads singleton () {
85
88
return ImageSingletons .lookup (JavaThreads .class );
86
89
}
87
90
91
+ private static final Unsafe UNSAFE = GraalUnsafeAccess .getUnsafe ();
92
+
88
93
/** The {@link java.lang.Thread} for the {@link IsolateThread}. */
89
94
static final FastThreadLocalObject <Thread > currentThread = FastThreadLocalFactory .createObject (Thread .class , "JavaThreads.currentThread" ).setMaxOffset (FastThreadLocal .BYTE_OFFSET );
90
95
@@ -183,16 +188,6 @@ public static boolean isInterrupted(Thread thread) {
183
188
return toTarget (thread ).interruptedJDK11OrEarlier ;
184
189
}
185
190
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
-
196
191
@ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
197
192
protected static boolean wasStartedByCurrentIsolate (IsolateThread thread ) {
198
193
Thread javaThread = currentThread .get (thread );
@@ -430,8 +425,7 @@ public static void detachThread(IsolateThread vmThread) {
430
425
VMThreads .THREAD_MUTEX .assertIsOwner ("Must hold the THREAD_MUTEX." );
431
426
432
427
Thread thread = currentThread .get (vmThread );
433
- ParkEvent .detach (getUnsafeParkEvent (thread ));
434
- ParkEvent .detach (getSleepParkEvent (thread ));
428
+ toTarget (thread ).threadData .detach ();
435
429
toTarget (thread ).isolateThread = WordFactory .nullPointer ();
436
430
if (!thread .isDaemon ()) {
437
431
nonDaemonThreads .decrementAndGet ();
@@ -747,20 +741,21 @@ static void initializeNewThread(
747
741
/** Interruptibly park the current thread. */
748
742
static void park () {
749
743
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 ();
751
745
if (isInterrupted (thread )) { // avoid state changes and synchronization
752
746
return ;
753
747
}
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 ();
759
750
// Change the Java thread state while parking.
760
- final int oldStatus = JavaThreads .getThreadStatus (thread );
751
+ int oldStatus = JavaThreads .getThreadStatus (thread );
761
752
int newStatus = MonitorSupport .singleton ().maybeAdjustNewParkStatus (ThreadStatus .PARKED );
762
753
JavaThreads .setThreadStatus (thread , newStatus );
763
754
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
+ */
764
759
parkEvent .condWait ();
765
760
} finally {
766
761
JavaThreads .setThreadStatus (thread , oldStatus );
@@ -770,19 +765,20 @@ static void park() {
770
765
/** Interruptibly park the current thread for the given number of nanoseconds. */
771
766
static void park (long delayNanos ) {
772
767
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 ();
774
769
if (isInterrupted (thread )) { // avoid state changes and synchronization
775
770
return ;
776
771
}
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 );
783
775
int newStatus = MonitorSupport .singleton ().maybeAdjustNewParkStatus (ThreadStatus .PARKED_TIMED );
784
776
JavaThreads .setThreadStatus (thread , newStatus );
785
777
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
+ */
786
782
parkEvent .condTimedWait (delayNanos );
787
783
} finally {
788
784
JavaThreads .setThreadStatus (thread , oldStatus );
@@ -796,12 +792,14 @@ static void park(long delayNanos) {
796
792
* @see #park(long)
797
793
*/
798
794
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
+ }
805
803
}
806
804
807
805
/** Sleep for the given number of nanoseconds, dealing with early wakeups and interruptions. */
@@ -817,23 +815,31 @@ static void sleep(long millis) throws InterruptedException {
817
815
818
816
static void sleep0 (long delayNanos ) {
819
817
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 ( );
822
820
sleepEvent .reset ();
821
+
823
822
/*
824
823
* It is critical to reset the event *before* checking for an interrupt to avoid losing a
825
824
* 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.
830
830
*/
831
+ UNSAFE .fullFence ();
832
+
831
833
if (isInterrupted (thread )) {
832
834
return ; // likely leaves a stale unpark which will be reset before the next sleep()
833
835
}
834
836
final int oldStatus = JavaThreads .getThreadStatus (thread );
835
837
JavaThreads .setThreadStatus (thread , ThreadStatus .SLEEPING );
836
838
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
+ */
837
843
sleepEvent .condTimedWait (delayNanos );
838
844
} finally {
839
845
JavaThreads .setThreadStatus (thread , oldStatus );
@@ -846,16 +852,32 @@ static void sleep0(long delayNanos) {
846
852
* @see #sleep(long)
847
853
*/
848
854
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
+ }
852
865
}
853
866
}
854
867
855
868
static boolean isAlive (int threadStatus ) {
856
869
return !(threadStatus == ThreadStatus .NEW || threadStatus == ThreadStatus .TERMINATED );
857
870
}
858
871
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
+
859
881
/**
860
882
* Builds a list of all application threads. This must be done in a VM operation because only
861
883
* there we are allowed to allocate Java memory while holding the {@link VMThreads#THREAD_MUTEX}
0 commit comments