File: AllThreadIds.java

package info (click to toggle)
openjdk-11 11.0.4%2B11-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 757,028 kB
  • sloc: java: 5,016,041; xml: 1,191,974; cpp: 934,731; ansic: 555,697; sh: 24,299; objc: 12,703; python: 3,602; asm: 3,415; makefile: 2,772; awk: 351; sed: 172; perl: 114; jsp: 24; csh: 3
file content (375 lines) | stat: -rw-r--r-- 12,558 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug     4530538
 * @key intermittent
 * @summary Basic unit test of ThreadMXBean.getAllThreadIds()
 * @author  Alexei Guibadoulline and Mandy Chung
 *
 * @run main/othervm AllThreadIds
 */

import java.lang.management.*;
import java.time.Instant;
import java.util.concurrent.Phaser;
import java.util.function.Supplier;

public class AllThreadIds {
    /**
     * A supplier wrapper for the delayed format printing.
     * The supplied value will have to be formatted as <em>$s</em>
     * @param <T> The wrapped type
     */
    private static final class ArgWrapper<T> {
        private final Supplier<T> val;

        public ArgWrapper(Supplier<T> val) {
            this.val = val;
        }

        @Override
        public String toString() {
            T resolved = val.get();
            return resolved != null ? resolved.toString() : null;
        }
    }

    static final int DAEMON_THREADS = 20;
    static final int USER_THREADS = 5;
    static final int ALL_THREADS = DAEMON_THREADS + USER_THREADS;
    private static final boolean live[] = new boolean[ALL_THREADS];
    private static final Thread allThreads[] = new Thread[ALL_THREADS];
    private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    private static boolean testFailed = false;
    private static boolean trace = false;

    private static long prevTotalThreadCount = 0;
    private static int prevLiveThreadCount = 0;
    private static int prevPeakThreadCount = 0;

    private static final Phaser startupCheck = new Phaser(ALL_THREADS + 1);

    private static void printThreadList() {
        long[] list = mbean.getAllThreadIds();
        for (int i = 1; i <= list.length; i++) {
            System.out.println(i + ": Thread id = " + list[i-1]);
        }
        for (int i = 0; i < ALL_THREADS; i++) {
            Thread t = allThreads[i];
            System.out.println(t.getName() + " Id = " + t.getId() +
                " die = " + live[i] +
                " alive = " + t.isAlive());
        }
    }

    private static void checkInitialState() throws Exception {
        updateCounters();
        checkThreadCount(0, 0);
    }

    private static void checkAllThreadsAlive() throws Exception {
        updateCounters();

        // Start all threads and wait to be sure they all are alive
        for (int i = 0; i < ALL_THREADS; i++) {
            setLive(i, true);
            allThreads[i] = new MyThread(i);
            allThreads[i].setDaemon(i < DAEMON_THREADS);
            allThreads[i].start();
        }
        // wait until all threads are started.
        startupCheck.arriveAndAwaitAdvance();

        checkThreadCount(ALL_THREADS, 0);
        if (trace) {
            printThreadList();
        }
        // Check mbean now. All threads must appear in getAllThreadIds() list
        long[] list = mbean.getAllThreadIds();

        for (int i = 0; i < ALL_THREADS; i++) {
            long expectedId = allThreads[i].getId();
            boolean found = false;

            if (trace) {
                System.out.print("Looking for thread with id " + expectedId);
            }
            for (int j = 0; j < list.length; j++) {
                if (expectedId == list[j]) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                testFailed = true;
            }
            if (trace) {
                if (!found) {
                    System.out.print(". TEST FAILED.");
                }
                System.out.println();
            }
        }
        if (trace) {
            System.out.println();
        }
    }

    private static void checkDaemonThreadsDead() throws Exception {
        updateCounters();

        // Stop daemon threads, wait to be sure they all are dead, and check
        // that they disappeared from getAllThreadIds() list
        for (int i = 0; i < DAEMON_THREADS; i++) {
            setLive(i, false);
        }

        // make sure the daemon threads are completely dead
        joinDaemonThreads();

        // and check the reported thread count
        checkThreadCount(0, DAEMON_THREADS);

        // Check mbean now
        long[] list = mbean.getAllThreadIds();

        for (int i = 0; i < ALL_THREADS; i++) {
            long expectedId = allThreads[i].getId();
            boolean found = false;
            boolean alive = (i >= DAEMON_THREADS);

            if (trace) {
                System.out.print("Looking for thread with id " + expectedId +
                    (alive ? " expected alive." : " expected terminated."));
            }
            for (int j = 0; j < list.length; j++) {
                if (expectedId == list[j]) {
                    found = true;
                    break;
                }
            }

            if (alive != found) {
                testFailed = true;
            }
            if (trace) {
                if (alive != found) {
                    System.out.println(" TEST FAILED.");
                } else {
                    System.out.println();
                }
            }
        }
    }

    private static void checkAllThreadsDead() throws Exception {
        updateCounters();

        // Stop all threads and wait to be sure they all are dead
        for (int i = DAEMON_THREADS; i < ALL_THREADS; i++) {
            setLive(i, false);
        }

        // make sure the non-daemon threads are completely dead
        joinNonDaemonThreads();

        // and check the thread count
        checkThreadCount(0, ALL_THREADS - DAEMON_THREADS);
    }

    private static void checkThreadCount(int numNewThreads,
                                         int numTerminatedThreads)
        throws Exception {

        checkLiveThreads(numNewThreads, numTerminatedThreads);
        checkPeakThreads(numNewThreads);
        checkTotalThreads(numNewThreads);
        checkThreadIds();
    }

    private static void checkLiveThreads(int numNewThreads,
                                         int numTerminatedThreads)
        throws InterruptedException {
        int diff = numNewThreads - numTerminatedThreads;

        waitTillEquals(
            diff + prevLiveThreadCount,
            ()->(long)mbean.getThreadCount(),
            "Unexpected number of live threads: " +
                " Prev live = %1$d Current live = ${provided} Threads added = %2$d" +
                " Threads terminated = %3$d",
            ()->prevLiveThreadCount,
            ()->numNewThreads,
            ()->numTerminatedThreads
        );
    }

    private static void checkPeakThreads(int numNewThreads)
        throws InterruptedException {

        waitTillEquals(numNewThreads + prevPeakThreadCount,
            ()->(long)mbean.getPeakThreadCount(),
            "Unexpected number of peak threads: " +
                " Prev peak = %1$d Current peak = ${provided} Threads added = %2$d",
            ()->prevPeakThreadCount,
            ()->numNewThreads
        );
    }

    private static void checkTotalThreads(int numNewThreads)
        throws InterruptedException {

        waitTillEquals(numNewThreads + prevTotalThreadCount,
            ()->mbean.getTotalStartedThreadCount(),
            "Unexpected number of total threads: " +
                " Prev Total = %1$d Current Total = ${provided} Threads added = %2$d",
            ()->prevTotalThreadCount,
            ()->numNewThreads
        );
    }

    private static void checkThreadIds() throws InterruptedException {
        long[] list = mbean.getAllThreadIds();

        waitTillEquals(
            list.length,
            ()->(long)mbean.getThreadCount(),
            "Array length returned by " +
                "getAllThreadIds() = %1$d not matched count = ${provided}",
            ()->list.length
        );
    }

    /**
     * Waits till the <em>expectedVal</em> equals to the <em>retrievedVal</em>.
     * It will report a status message on the first occasion of the value mismatch
     * and then, subsequently, when the <em>retrievedVal</em> value changes.
     * @param expectedVal The value to wait for
     * @param retrievedVal The supplier of the value to check against the <em>expectedVal</em>
     * @param msgFormat The formatted message to be printed in case of mismatch
     * @param msgArgs The parameters to the formatted message
     * @throws InterruptedException
     */
    private static void waitTillEquals(long expectedVal, Supplier<Long> retrievedVal,
                                        String msgFormat, Supplier<Object> ... msgArgs)
        throws InterruptedException {
        Object[] args = null;

        long countPrev = -1;
        while (true) {
            Long count = retrievedVal.get();
            if (count == expectedVal) break;
            if (countPrev == -1 || countPrev != count) {
                if (args == null) {
                    args = new Object[msgArgs.length];
                    for(int i=0; i < msgArgs.length; i++) {
                        args[i] = new ArgWrapper<>((Supplier<Object>)msgArgs[i]);
                    }
                }
                System.err.format("TS: %s\n", Instant.now());
                System.err.format(
                    msgFormat
                        .replace("${provided}", String.valueOf(count))
                        .replace("$d", "$s"),
                    args
                ).flush();
                printThreadList();
                System.err.println("\nRetrying ...\n");
            }
            countPrev = count;
            Thread.sleep(1);
        }
    }

    private static void updateCounters() {
        prevTotalThreadCount = mbean.getTotalStartedThreadCount();
        prevLiveThreadCount = mbean.getThreadCount();
        prevPeakThreadCount = mbean.getPeakThreadCount();
    }

    public static void main(String args[]) throws Exception {
        if (args.length > 0 && args[0].equals("trace")) {
            trace = true;
        }

        checkInitialState();
        checkAllThreadsAlive();
        checkDaemonThreadsDead();
        checkAllThreadsDead();

        if (testFailed)
            throw new RuntimeException("TEST FAILED.");

        System.out.println("Test passed.");
    }

    private static void joinDaemonThreads() throws InterruptedException {
        for (int i = 0; i < DAEMON_THREADS; i++) {
            allThreads[i].join();
        }
    }

    private static void joinNonDaemonThreads() throws InterruptedException {
        for (int i = DAEMON_THREADS; i < ALL_THREADS; i++) {
            allThreads[i].join();
        }
    }

    private static void setLive(int i, boolean val) {
        synchronized(live) {
            live[i] = val;
        }
    }

    private static boolean isLive(int i) {
        synchronized(live) {
            return live[i];
        }
    }

    // The MyThread thread lives as long as correspondent live[i] value is true
    private static class MyThread extends Thread {
        int id;

        MyThread(int id) {
            this.id = id;
        }

        public void run() {
            // signal started
            startupCheck.arrive();
            while (isLive(id)) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    System.out.println("Unexpected exception is thrown.");
                    e.printStackTrace(System.out);
                    testFailed = true;
                }
            }
        }
    }
}