File: StructuredThreadDumpTest.java

package info (click to toggle)
openjdk-21 21.0.8%2B9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 823,976 kB
  • sloc: java: 5,613,338; xml: 1,643,607; cpp: 1,296,296; ansic: 420,291; asm: 404,850; objc: 20,994; sh: 15,271; javascript: 11,245; python: 6,895; makefile: 2,362; perl: 357; awk: 351; sed: 172; jsp: 24; csh: 3
file content (199 lines) | stat: -rw-r--r-- 7,997 bytes parent folder | download | duplicates (4)
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
/*
 * Copyright (c) 2022, 2023, 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 8284199 8296779 8306647
 * @summary Test thread dumps with StructuredTaskScope
 * @enablePreview
 * @library /test/lib
 * @run junit/othervm StructuredThreadDumpTest
 */

import java.util.concurrent.StructuredTaskScope;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Stream;
import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
import jdk.test.lib.threaddump.ThreadDump;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class StructuredThreadDumpTest {

    /**
     * Test that a thread dump with a tree of task scopes contains a thread grouping for
     * each task scope.
     */
    @Test
    void testTree() throws Exception {
        ThreadFactory factory = Thread.ofVirtual().factory();
        try (var scope = new StructuredTaskScope<>("scope", factory)) {
            Thread thread1 = fork(scope, "child-scope-A");
            Thread thread2 = fork(scope, "child-scope-B");
            try {
                ThreadDump threadDump = threadDump();

                // thread dump should have a thread container for each scope
                var rootContainer = threadDump.rootThreadContainer();
                var container1 = threadDump.findThreadContainer("scope").orElseThrow();
                var container2 = threadDump.findThreadContainer("child-scope-A").orElseThrow();
                var container3 = threadDump.findThreadContainer("child-scope-B").orElseThrow();

                // check parents
                assertFalse(rootContainer.parent().isPresent());
                assertTrue(container1.parent().get() == rootContainer);
                assertTrue(container2.parent().get() == container1);
                assertTrue(container3.parent().get() == container1);

                // check owners
                assertFalse(rootContainer.owner().isPresent());
                assertTrue(container1.owner().getAsLong() == Thread.currentThread().threadId());
                assertTrue(container2.owner().getAsLong() == thread1.threadId());
                assertTrue(container3.owner().getAsLong() == thread2.threadId());

                // thread1 and threads2 should be in threads array of "scope"
                container1.findThread(thread1.threadId()).orElseThrow();
                container1.findThread(thread2.threadId()).orElseThrow();

            } finally {
                scope.shutdown();
                scope.join();
            }
        }
    }

    /**
     * Test that a thread dump with nested tasks scopes contains a thread grouping for
     * each task scope.
     */
    @Test
    void testNested() throws Exception {
        ThreadFactory factory = Thread.ofVirtual().factory();
        try (var scope1 = new StructuredTaskScope<>("scope-A", factory)) {
            Thread thread1 = fork(scope1);

            try (var scope2 = new StructuredTaskScope<>("scope-B", factory)) {
                Thread thread2 = fork(scope2);
                try {
                    ThreadDump threadDump = threadDump();

                    // thread dump should have a thread container for both scopes
                    var rootContainer = threadDump.rootThreadContainer();
                    var container1 = threadDump.findThreadContainer("scope-A").orElseThrow();
                    var container2 = threadDump.findThreadContainer("scope-B").orElseThrow();

                    // check parents
                    assertFalse(rootContainer.parent().isPresent());
                    assertTrue(container1.parent().get() == rootContainer);
                    assertTrue(container2.parent().get() == container1);

                    // check owners
                    long tid = Thread.currentThread().threadId();
                    assertFalse(rootContainer.owner().isPresent());
                    assertTrue(container1.owner().getAsLong() == tid);
                    assertTrue(container2.owner().getAsLong() == tid);

                    // thread1 should be in threads array of "scope-A"
                    container1.findThread(thread1.threadId()).orElseThrow();

                    // thread2 should be in threads array of "scope-B"
                    container2.findThread(thread2.threadId()).orElseThrow();

                } finally {
                    scope2.shutdown();
                    scope2.join();
                }
            } finally {
                scope1.shutdown();
                scope1.join();
            }
        }
    }

    /**
     * Generates a JSON formatted thread dump to a temporary file, prints it to standard
     * output, parses the JSON text and returns a ThreadDump object for the thread dump.
     */
    private static ThreadDump threadDump() throws IOException {
        Path dir = Path.of(".").toAbsolutePath();
        Path file = Files.createTempFile(dir, "threadump", "json");
        Files.delete(file);
        ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
                .dumpThreads(file.toString(), ThreadDumpFormat.JSON);

        try (Stream<String> stream = Files.lines(file)) {
            stream.forEach(System.out::println);
        }

        String jsonText = Files.readString(file);
        return ThreadDump.parse(jsonText);
    }

    /**
     * Forks a subtask in the given scope that parks, returning the Thread that executes
     * the subtask.
     */
    private static Thread fork(StructuredTaskScope<Object> scope) throws Exception {
        var ref = new AtomicReference<Thread>();
        scope.fork(() -> {
            ref.set(Thread.currentThread());
            LockSupport.park();
            return null;
        });
        Thread thread;
        while ((thread = ref.get()) == null) {
            Thread.sleep(10);
        }
        return thread;
    }

    /**
     * Forks a subtask in the given scope. The subtask creates a new child scope with
     * the given name, then parks. This method returns Thread that executes the subtask.
     */
    private static Thread fork(StructuredTaskScope<Object> scope,
                               String childScopeName) throws Exception {
        var ref = new AtomicReference<Thread>();
        scope.fork(() -> {
            ThreadFactory factory = Thread.ofVirtual().factory();
            try (var childScope = new StructuredTaskScope<Object>(childScopeName, factory)) {
                ref.set(Thread.currentThread());
                LockSupport.park();
            }
            return null;
        });
        Thread thread;
        while ((thread = ref.get()) == null) {
            Thread.sleep(10);
        }
        return thread;
    }

}