44
44
import java .util .concurrent .ForkJoinWorkerThread ;
45
45
import java .util .concurrent .ScheduledThreadPoolExecutor ;
46
46
import java .util .concurrent .ThreadPoolExecutor ;
47
- import java .util .concurrent .atomic .AtomicBoolean ;
48
47
import java .util .concurrent .atomic .AtomicInteger ;
49
48
50
49
import org .graalvm .nativeimage .CurrentIsolate ;
@@ -550,8 +549,8 @@ public void closeOSThreadHandle(OSThreadHandle threadHandle) {
550
549
* teardown logic (see {@link #isVMInternalThread(IsolateThread)}). Waits until the interrupted
551
550
* threads detach.
552
551
*/
553
- public static boolean tearDownOtherThreads () {
554
- final Log trace = Log .noopLog ().string ("[PlatformThreads.tearDownPlatformThreads:" ).newline ().flush ();
552
+ public static void tearDownOtherThreads () {
553
+ Log trace = Log .noopLog ().string ("[PlatformThreads.tearDownPlatformThreads:" ).newline ().flush ();
555
554
556
555
/*
557
556
* Set tear-down flag for new Java threads that have already been started on an OS level,
@@ -626,46 +625,36 @@ public static boolean tearDownOtherThreads() {
626
625
trace .string (" shutdown initiated: " ).object (pool ).newline ().flush ();
627
626
}
628
627
629
- final boolean result = waitForTearDown ();
630
- trace .string (" returns: " ).bool (result ).string ("]" ).newline ().flush ();
631
- return result ;
628
+ waitForTearDown ();
632
629
}
633
630
634
631
/** Wait (im)patiently for the thread list to drain. */
635
- private static boolean waitForTearDown () {
632
+ private static void waitForTearDown () {
636
633
assert !isVMInternalThread (CurrentIsolate .getCurrentThread ()) : "we count the threads until only the current one remains" ;
637
634
638
- final Log trace = Log .noopLog ().string ("[PlatformThreads.waitForTearDown:" ).newline ();
639
- final long warningNanos = SubstrateOptions .getTearDownWarningNanos ();
640
- final String warningMessage = "PlatformThreads.waitForTearDown is taking too long." ;
641
- final long failureNanos = SubstrateOptions .getTearDownFailureNanos ();
642
- final String failureMessage = "PlatformThreads.waitForTearDown took too long." ;
643
- final long startNanos = System .nanoTime ();
644
- long loopNanos = startNanos ;
645
- final AtomicBoolean printLaggards = new AtomicBoolean (false );
646
- final Log counterLog = ((warningNanos == 0 ) ? trace : Log .log ());
647
- final CheckReadyForTearDownOperation operation = new CheckReadyForTearDownOperation (counterLog , printLaggards );
648
-
649
- for (; /* return */ ;) {
650
- final long previousLoopNanos = loopNanos ;
635
+ CheckReadyForTearDownOperation operation = new CheckReadyForTearDownOperation ();
636
+ long warningConfiguredNanos = SubstrateOptions .getTearDownWarningNanos ();
637
+ long failureConfiguredNanos = SubstrateOptions .getTearDownFailureNanos ();
638
+ long startNanos = System .nanoTime ();
639
+ long previousReportNanos = startNanos ;
640
+
641
+ while (true ) {
651
642
operation .enqueue ();
652
643
if (operation .isReadyForTearDown ()) {
653
- trace .string (" returns true]" ).newline ();
654
- return true ;
644
+ return ;
655
645
}
656
- loopNanos = TimeUtils . doNotLoopTooLong ( startNanos , loopNanos , warningNanos , warningMessage );
657
- final boolean fatallyTooLong = TimeUtils .maybeFatallyTooLong (startNanos , failureNanos , failureMessage );
658
- if (fatallyTooLong ) {
659
- trace . string ("Took too long to tear down the VM." ). newline ( );
660
- /*
661
- * Debugging tip: Insert a `BreakpointNode.breakpoint()` here to stop in gdb or get
662
- * a core file with the thread stacks. Be careful about believing the stack traces,
663
- * though.
664
- */
665
- return false ;
646
+
647
+ long sinceStartNanos = TimeUtils .nanoSecondsSince (startNanos );
648
+ if (failureConfiguredNanos > 0 && TimeUtils . nanoTimeLessThan ( failureConfiguredNanos , sinceStartNanos ) ) {
649
+ throw VMError . shouldNotReachHere ("Took too long to tear down the VM." );
650
+ }
651
+
652
+ long sinceReportNanos = TimeUtils . nanoSecondsSince ( previousReportNanos );
653
+ if ( warningConfiguredNanos > 0 && TimeUtils . nanoTimeLessThan ( warningConfiguredNanos , sinceReportNanos )) {
654
+ operation . enablePrintLaggards ();
655
+ previousReportNanos += sinceReportNanos ;
666
656
}
667
- /* If I took too long, print the laggards next time around. */
668
- printLaggards .set (previousLoopNanos != loopNanos );
657
+
669
658
/* Loop impatiently waiting for threads to exit. */
670
659
Thread .yield ();
671
660
}
@@ -1175,14 +1164,15 @@ public void operate() {
1175
1164
* holding the {@link VMThreads#THREAD_MUTEX}.
1176
1165
*/
1177
1166
private static class CheckReadyForTearDownOperation extends JavaVMOperation {
1178
- private final Log trace ;
1179
- private final AtomicBoolean printLaggards ;
1167
+ private boolean printLaggards ;
1180
1168
private boolean readyForTearDown ;
1181
1169
1182
- CheckReadyForTearDownOperation (Log trace , AtomicBoolean printLaggards ) {
1170
+ CheckReadyForTearDownOperation () {
1183
1171
super (VMOperationInfos .get (CheckReadyForTearDownOperation .class , "Check ready for teardown" , SystemEffect .NONE ));
1184
- this .trace = trace ;
1185
- this .printLaggards = printLaggards ;
1172
+ }
1173
+
1174
+ void enablePrintLaggards () {
1175
+ printLaggards = true ;
1186
1176
}
1187
1177
1188
1178
boolean isReadyForTearDown () {
@@ -1191,47 +1181,82 @@ boolean isReadyForTearDown() {
1191
1181
1192
1182
@ Override
1193
1183
public void operate () {
1194
- int attachedCount = 0 ;
1195
- int unattachedStartedCount ;
1196
1184
VMMutex lock = VMThreads .THREAD_MUTEX .lock ();
1197
1185
try {
1198
- for (IsolateThread isolateThread = VMThreads .firstThread (); isolateThread .isNonNull (); isolateThread = VMThreads .nextThread (isolateThread )) {
1199
- if (isVMInternalThread (isolateThread )) {
1200
- continue ;
1201
- }
1186
+ readyForTearDown = isReadyForTeardown ();
1187
+ } finally {
1188
+ lock .unlock ();
1189
+ }
1190
+ }
1202
1191
1203
- attachedCount ++;
1204
- if ( printLaggards . get () && trace . isEnabled () && isolateThread != queuingThread ) {
1205
- trace . string ( " laggard isolateThread: " ). hex ( isolateThread ) ;
1206
- final Thread thread = PlatformThreads . fromVMThread ( isolateThread );
1207
- if ( thread != null ) {
1208
- final String name = thread . getName ();
1209
- final Thread . State status = thread . getState () ;
1210
- final boolean interruptedStatus = JavaThreads . isInterrupted ( thread );
1211
- trace . string ( " thread.getName(): " ). string ( name )
1212
- . string ( " interruptedStatus: " ). bool ( interruptedStatus )
1213
- . string ( " getState(): " ). string ( status . name ()). newline ();
1214
- for ( StackTraceElement e : thread . getStackTrace ()) {
1215
- trace . string ( e . toString ()). newline ();
1216
- }
1217
- }
1218
- trace . newline ().flush ( );
1192
+ private boolean isReadyForTeardown () {
1193
+ int attachedCount = 0 ;
1194
+ boolean printed = false ;
1195
+
1196
+ for ( IsolateThread thread = VMThreads . firstThread (); thread . isNonNull (); thread = VMThreads . nextThread ( thread ) ) {
1197
+ if ( isVMInternalThread ( thread )) {
1198
+ continue ;
1199
+ }
1200
+
1201
+ attachedCount ++;
1202
+
1203
+ /* Print some information about slow threads. */
1204
+ if ( printLaggards && thread != queuingThread ) {
1205
+ if (! printed ) {
1206
+ printed = true ;
1207
+ Log . log ().string ( "Teardown is taking too long" ). redent ( true );
1219
1208
}
1209
+
1210
+ printThreadInfo (Log .log (), thread );
1220
1211
}
1212
+ }
1221
1213
1222
- /*
1223
- * Note: our counter for unattached started threads is not guarded by the threads
1224
- * mutex and its count could change or have changed within this block. Still, it is
1225
- * important that we hold the threads mutex when querying the counter value: a
1226
- * thread might start another thread and exit immediately after. By holding the
1227
- * threads lock, we prevent the exiting thread from detaching, and/or the starting
1228
- * thread from attaching, so we will never consider being ready for tear-down.
1229
- */
1230
- unattachedStartedCount = singleton ().unattachedStartedThreads .get ();
1231
- } finally {
1232
- lock .unlock ();
1214
+ if (printed ) {
1215
+ Log .log ().indent (false );
1216
+ }
1217
+
1218
+ /*
1219
+ * Note: our counter for unattached started threads is not guarded by the threads mutex
1220
+ * and its count could change or have changed within this block. Still, it is important
1221
+ * that we hold the threads mutex when querying the counter value: a thread might start
1222
+ * another thread and exit immediately after. By holding the threads lock, we prevent
1223
+ * the exiting thread from detaching, and/or the starting thread from attaching, so we
1224
+ * will never consider being ready for tear-down.
1225
+ */
1226
+ int unattachedStartedCount = singleton ().unattachedStartedThreads .get ();
1227
+
1228
+ printLaggards = false ;
1229
+ return (attachedCount == 1 && unattachedStartedCount == 0 );
1230
+ }
1231
+
1232
+ private static void printThreadInfo (Log log , IsolateThread thread ) {
1233
+ log .newline ().zhex (thread ).spaces (1 ).string (StatusSupport .getStatusString (thread ));
1234
+
1235
+ int safepointBehavior = VMThreads .SafepointBehavior .getSafepointBehaviorVolatile (thread );
1236
+ log .string (" (" ).string (VMThreads .SafepointBehavior .toString (safepointBehavior )).string (")" );
1237
+
1238
+ Thread threadObj = PlatformThreads .fromVMThread (thread );
1239
+ if (threadObj == null ) {
1240
+ log .string (" null" );
1241
+ } else {
1242
+ log .string (" \" " ).string (threadObj .getName ()).string ("\" - " ).zhex (Word .objectToUntrackedPointer (threadObj ));
1243
+
1244
+ Thread .State status = threadObj .getState ();
1245
+ log .string (" (" ).string (status .name ()).string (")" );
1246
+
1247
+ if (threadObj .isDaemon ()) {
1248
+ log .string (", daemon" );
1249
+ }
1250
+ if (JavaThreads .isInterrupted (threadObj )) {
1251
+ log .string (", interrupted" );
1252
+ }
1253
+ }
1254
+
1255
+ log .indent (true );
1256
+ for (StackTraceElement e : threadObj .getStackTrace ()) {
1257
+ log .string (e .toString ()).newline ();
1233
1258
}
1234
- readyForTearDown = ( attachedCount == 1 && unattachedStartedCount == 0 );
1259
+ log . redent ( false );
1235
1260
}
1236
1261
}
1237
1262
0 commit comments