|
9 | 9 | import java.util.concurrent.ConcurrentHashMap;
|
10 | 10 | import java.util.concurrent.ConcurrentMap;
|
11 | 11 | import java.util.concurrent.Executors;
|
| 12 | +import java.util.concurrent.ScheduledExecutorService; |
12 | 13 | import java.util.concurrent.TimeUnit;
|
13 | 14 | import java.util.concurrent.atomic.AtomicLong;
|
14 | 15 | import java.util.function.Consumer;
|
@@ -84,15 +85,24 @@ void report() {
|
84 | 85 | @SuppressWarnings("CanIgnoreReturnValueSuggester")
|
85 | 86 | private SupportabilityMetrics start() {
|
86 | 87 | if (agentDebugEnabled) {
|
87 |
| - Executors.newScheduledThreadPool( |
| 88 | + ScheduledExecutorService executor = |
| 89 | + Executors.newScheduledThreadPool( |
88 | 90 | 1,
|
89 | 91 | runnable -> {
|
90 | 92 | Thread result = new Thread(runnable, "supportability_metrics_reporter");
|
91 | 93 | result.setDaemon(true);
|
92 | 94 | result.setContextClassLoader(null);
|
93 | 95 | return result;
|
94 |
| - }) |
95 |
| - .scheduleAtFixedRate(this::report, 5, 5, TimeUnit.SECONDS); |
| 96 | + }); |
| 97 | + executor.scheduleAtFixedRate(this::report, 5, 5, TimeUnit.SECONDS); |
| 98 | + // the condition below will always be false, but by referencing the executor it ensures the |
| 99 | + // executor can't become unreachable in the middle of the scheduleAtFixedRate() method |
| 100 | + // execution above (and prior to the task being registered), which can lead to the executor |
| 101 | + // being terminated and scheduleAtFixedRate throwing a RejectedExecutionException |
| 102 | + // (see https://bugs.openjdk.org/browse/JDK-8145304) |
| 103 | + if (executor.isTerminated()) { |
| 104 | + throw new AssertionError(); |
| 105 | + } |
96 | 106 | }
|
97 | 107 | return this;
|
98 | 108 | }
|
|
0 commit comments