4444import java .util .concurrent .ForkJoinWorkerThread ;
4545import java .util .concurrent .ScheduledThreadPoolExecutor ;
4646import java .util .concurrent .ThreadPoolExecutor ;
47- import java .util .concurrent .atomic .AtomicBoolean ;
4847import java .util .concurrent .atomic .AtomicInteger ;
4948
5049import org .graalvm .nativeimage .CurrentIsolate ;
@@ -550,8 +549,8 @@ public void closeOSThreadHandle(OSThreadHandle threadHandle) {
550549 * teardown logic (see {@link #isVMInternalThread(IsolateThread)}). Waits until the interrupted
551550 * threads detach.
552551 */
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 ();
555554
556555 /*
557556 * 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() {
626625 trace .string (" shutdown initiated: " ).object (pool ).newline ().flush ();
627626 }
628627
629- final boolean result = waitForTearDown ();
630- trace .string (" returns: " ).bool (result ).string ("]" ).newline ().flush ();
631- return result ;
628+ waitForTearDown ();
632629 }
633630
634631 /** Wait (im)patiently for the thread list to drain. */
635- private static boolean waitForTearDown () {
632+ private static void waitForTearDown () {
636633 assert !isVMInternalThread (CurrentIsolate .getCurrentThread ()) : "we count the threads until only the current one remains" ;
637634
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 ) {
651642 operation .enqueue ();
652643 if (operation .isReadyForTearDown ()) {
653- trace .string (" returns true]" ).newline ();
654- return true ;
644+ return ;
655645 }
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 ;
666656 }
667- /* If I took too long, print the laggards next time around. */
668- printLaggards .set (previousLoopNanos != loopNanos );
657+
669658 /* Loop impatiently waiting for threads to exit. */
670659 Thread .yield ();
671660 }
@@ -1175,14 +1164,15 @@ public void operate() {
11751164 * holding the {@link VMThreads#THREAD_MUTEX}.
11761165 */
11771166 private static class CheckReadyForTearDownOperation extends JavaVMOperation {
1178- private final Log trace ;
1179- private final AtomicBoolean printLaggards ;
1167+ private boolean printLaggards ;
11801168 private boolean readyForTearDown ;
11811169
1182- CheckReadyForTearDownOperation (Log trace , AtomicBoolean printLaggards ) {
1170+ CheckReadyForTearDownOperation () {
11831171 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 ;
11861176 }
11871177
11881178 boolean isReadyForTearDown () {
@@ -1191,47 +1181,82 @@ boolean isReadyForTearDown() {
11911181
11921182 @ Override
11931183 public void operate () {
1194- int attachedCount = 0 ;
1195- int unattachedStartedCount ;
11961184 VMMutex lock = VMThreads .THREAD_MUTEX .lock ();
11971185 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+ }
12021191
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 );
12191208 }
1209+
1210+ printThreadInfo (Log .log (), thread );
12201211 }
1212+ }
12211213
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 ();
12331258 }
1234- readyForTearDown = ( attachedCount == 1 && unattachedStartedCount == 0 );
1259+ log . redent ( false );
12351260 }
12361261 }
12371262
0 commit comments