1 /*
2 * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @bug 8284199 8296779 8306647
27 * @summary Test thread dumps with StructuredTaskScope
28 * @enablePreview
29 * @library /test/lib
30 * @run junit/othervm StructuredThreadDumpTest
31 */
32
33 import java.util.concurrent.StructuredTaskScope;
34 import java.io.IOException;
35 import java.lang.management.ManagementFactory;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.util.concurrent.ThreadFactory;
39 import java.util.concurrent.atomic.AtomicReference;
40 import java.util.concurrent.locks.LockSupport;
41 import java.util.stream.Stream;
42 import com.sun.management.HotSpotDiagnosticMXBean;
43 import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
44 import jdk.test.lib.threaddump.ThreadDump;
45 import org.junit.jupiter.api.Test;
46 import static org.junit.jupiter.api.Assertions.*;
47
48 class StructuredThreadDumpTest {
49
50 /**
51 * Test that a thread dump with a tree of task scopes contains a thread grouping for
52 * each task scope.
53 */
54 @Test
55 void testTree() throws Exception {
56 ThreadFactory factory = Thread.ofVirtual().factory();
57 try (var scope = new StructuredTaskScope<>("scope", factory)) {
58 Thread thread1 = fork(scope, "child-scope-A");
59 Thread thread2 = fork(scope, "child-scope-B");
60 try {
61 ThreadDump threadDump = threadDump();
62
63 // thread dump should have a thread container for each scope
64 var rootContainer = threadDump.rootThreadContainer();
65 var container1 = threadDump.findThreadContainer("scope").orElseThrow();
66 var container2 = threadDump.findThreadContainer("child-scope-A").orElseThrow();
67 var container3 = threadDump.findThreadContainer("child-scope-B").orElseThrow();
68
69 // check parents
70 assertFalse(rootContainer.parent().isPresent());
71 assertTrue(container1.parent().get() == rootContainer);
72 assertTrue(container2.parent().get() == container1);
73 assertTrue(container3.parent().get() == container1);
74
75 // check owners
76 assertFalse(rootContainer.owner().isPresent());
77 assertTrue(container1.owner().getAsLong() == Thread.currentThread().threadId());
78 assertTrue(container2.owner().getAsLong() == thread1.threadId());
79 assertTrue(container3.owner().getAsLong() == thread2.threadId());
80
81 // thread1 and threads2 should be in threads array of "scope"
82 container1.findThread(thread1.threadId()).orElseThrow();
83 container1.findThread(thread2.threadId()).orElseThrow();
84
85 } finally {
86 scope.shutdown();
87 scope.join();
88 }
89 }
90 }
91
92 /**
93 * Test that a thread dump with nested tasks scopes contains a thread grouping for
94 * each task scope.
95 */
96 @Test
97 void testNested() throws Exception {
98 ThreadFactory factory = Thread.ofVirtual().factory();
99 try (var scope1 = new StructuredTaskScope<>("scope-A", factory)) {
100 Thread thread1 = fork(scope1);
101
102 try (var scope2 = new StructuredTaskScope<>("scope-B", factory)) {
103 Thread thread2 = fork(scope2);
104 try {
105 ThreadDump threadDump = threadDump();
106
107 // thread dump should have a thread container for both scopes
108 var rootContainer = threadDump.rootThreadContainer();
109 var container1 = threadDump.findThreadContainer("scope-A").orElseThrow();
110 var container2 = threadDump.findThreadContainer("scope-B").orElseThrow();
111
112 // check parents
113 assertFalse(rootContainer.parent().isPresent());
114 assertTrue(container1.parent().get() == rootContainer);
115 assertTrue(container2.parent().get() == container1);
116
117 // check owners
118 long tid = Thread.currentThread().threadId();
119 assertFalse(rootContainer.owner().isPresent());
120 assertTrue(container1.owner().getAsLong() == tid);
121 assertTrue(container2.owner().getAsLong() == tid);
122
123 // thread1 should be in threads array of "scope-A"
124 container1.findThread(thread1.threadId()).orElseThrow();
125
126 // thread2 should be in threads array of "scope-B"
127 container2.findThread(thread2.threadId()).orElseThrow();
128
129 } finally {
130 scope2.shutdown();
131 scope2.join();
132 }
133 } finally {
134 scope1.shutdown();
135 scope1.join();
136 }
137 }
138 }
139
140 /**
141 * Generates a JSON formatted thread dump to a temporary file, prints it to standard
142 * output, parses the JSON text and returns a ThreadDump object for the thread dump.
143 */
144 private static ThreadDump threadDump() throws IOException {
145 Path dir = Path.of(".").toAbsolutePath();
146 Path file = Files.createTempFile(dir, "threadump", "json");
147 Files.delete(file);
148 ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
149 .dumpThreads(file.toString(), ThreadDumpFormat.JSON);
150
151 try (Stream<String> stream = Files.lines(file)) {
152 stream.forEach(System.out::println);
153 }
154
155 String jsonText = Files.readString(file);
156 return ThreadDump.parse(jsonText);
157 }
158
159 /**
160 * Forks a subtask in the given scope that parks, returning the Thread that executes
161 * the subtask.
162 */
163 private static Thread fork(StructuredTaskScope<Object> scope) throws Exception {
164 var ref = new AtomicReference<Thread>();
165 scope.fork(() -> {
166 ref.set(Thread.currentThread());
167 LockSupport.park();
168 return null;
169 });
170 Thread thread;
171 while ((thread = ref.get()) == null) {
172 Thread.sleep(10);
173 }
174 return thread;
175 }
176
177 /**
178 * Forks a subtask in the given scope. The subtask creates a new child scope with
179 * the given name, then parks. This method returns Thread that executes the subtask.
180 */
181 private static Thread fork(StructuredTaskScope<Object> scope,
182 String childScopeName) throws Exception {
183 var ref = new AtomicReference<Thread>();
184 scope.fork(() -> {
185 ThreadFactory factory = Thread.ofVirtual().factory();
186 try (var childScope = new StructuredTaskScope<Object>(childScopeName, factory)) {
187 ref.set(Thread.currentThread());
188 LockSupport.park();
189 }
190 return null;
191 });
192 Thread thread;
193 while ((thread = ref.get()) == null) {
194 Thread.sleep(10);
195 }
196 return thread;
197 }
198
199 }
|
1 /*
2 * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @bug 8284199 8296779 8306647
27 * @summary Test thread dumps with StructuredTaskScope
28 * @enablePreview
29 * @library /test/lib
30 * @run junit/othervm StructuredThreadDumpTest
31 */
32
33 import java.util.concurrent.StructuredTaskScope;
34 import java.util.concurrent.StructuredTaskScope.Joiner;
35 import java.io.IOException;
36 import java.lang.management.ManagementFactory;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.concurrent.ThreadFactory;
40 import java.util.concurrent.atomic.AtomicReference;
41 import java.util.concurrent.locks.LockSupport;
42 import java.util.stream.Stream;
43 import com.sun.management.HotSpotDiagnosticMXBean;
44 import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
45 import jdk.test.lib.threaddump.ThreadDump;
46 import org.junit.jupiter.api.Test;
47 import static org.junit.jupiter.api.Assertions.*;
48
49 class StructuredThreadDumpTest {
50
51 /**
52 * Test that a thread dump with a tree of task scopes contains a thread grouping for
53 * each task scope.
54 */
55 @Test
56 void testTree() throws Exception {
57 try (var scope = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope"))) {
58 Thread thread1 = fork(scope, "child-scope-A");
59 Thread thread2 = fork(scope, "child-scope-B");
60 try {
61 ThreadDump threadDump = threadDump();
62
63 // thread dump should have a thread container for each scope
64 var rootContainer = threadDump.rootThreadContainer();
65 var container1 = threadDump.findThreadContainer("scope").orElseThrow();
66 var container2 = threadDump.findThreadContainer("child-scope-A").orElseThrow();
67 var container3 = threadDump.findThreadContainer("child-scope-B").orElseThrow();
68
69 // check parents
70 assertFalse(rootContainer.parent().isPresent());
71 assertTrue(container1.parent().get() == rootContainer);
72 assertTrue(container2.parent().get() == container1);
73 assertTrue(container3.parent().get() == container1);
74
75 // check owners
76 assertFalse(rootContainer.owner().isPresent());
77 assertTrue(container1.owner().getAsLong() == Thread.currentThread().threadId());
78 assertTrue(container2.owner().getAsLong() == thread1.threadId());
79 assertTrue(container3.owner().getAsLong() == thread2.threadId());
80
81 // thread1 and threads2 should be in threads array of "scope"
82 container1.findThread(thread1.threadId()).orElseThrow();
83 container1.findThread(thread2.threadId()).orElseThrow();
84
85 } finally {
86 LockSupport.unpark(thread1);
87 LockSupport.unpark(thread2);
88 scope.join();
89 }
90 }
91 }
92
93 /**
94 * Test that a thread dump with nested tasks scopes contains a thread grouping for
95 * each task scope.
96 */
97 @Test
98 void testNested() throws Exception {
99 try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope-A"))) {
100 Thread thread1 = fork(scope1);
101
102 try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope-B"))) {
103 Thread thread2 = fork(scope2);
104 try {
105 ThreadDump threadDump = threadDump();
106
107 // thread dump should have a thread container for both scopes
108 var rootContainer = threadDump.rootThreadContainer();
109 var container1 = threadDump.findThreadContainer("scope-A").orElseThrow();
110 var container2 = threadDump.findThreadContainer("scope-B").orElseThrow();
111
112 // check parents
113 assertFalse(rootContainer.parent().isPresent());
114 assertTrue(container1.parent().get() == rootContainer);
115 assertTrue(container2.parent().get() == container1);
116
117 // check owners
118 long tid = Thread.currentThread().threadId();
119 assertFalse(rootContainer.owner().isPresent());
120 assertTrue(container1.owner().getAsLong() == tid);
121 assertTrue(container2.owner().getAsLong() == tid);
122
123 // thread1 should be in threads array of "scope-A"
124 container1.findThread(thread1.threadId()).orElseThrow();
125
126 // thread2 should be in threads array of "scope-B"
127 container2.findThread(thread2.threadId()).orElseThrow();
128
129 } finally {
130 LockSupport.unpark(thread2);
131 scope2.join();
132 }
133 } finally {
134 LockSupport.unpark(thread1);
135 scope1.join();
136 }
137 }
138 }
139
140 /**
141 * Generates a JSON formatted thread dump to a temporary file, prints it to standard
142 * output, parses the JSON text and returns a ThreadDump object for the thread dump.
143 */
144 private static ThreadDump threadDump() throws IOException {
145 Path dir = Path.of(".").toAbsolutePath();
146 Path file = Files.createTempFile(dir, "threadump", "json");
147 Files.delete(file);
148 ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
149 .dumpThreads(file.toString(), ThreadDumpFormat.JSON);
150
151 try (Stream<String> stream = Files.lines(file)) {
152 stream.forEach(System.out::println);
153 }
154
155 String jsonText = Files.readString(file);
156 return ThreadDump.parse(jsonText);
157 }
158
159 /**
160 * Forks a subtask in the given scope that parks, returning the Thread that executes
161 * the subtask.
162 */
163 private static Thread fork(StructuredTaskScope<Object, Void> scope) throws Exception {
164 var ref = new AtomicReference<Thread>();
165 scope.fork(() -> {
166 ref.set(Thread.currentThread());
167 LockSupport.park();
168 return null;
169 });
170 Thread thread;
171 while ((thread = ref.get()) == null) {
172 Thread.sleep(10);
173 }
174 return thread;
175 }
176
177 /**
178 * Forks a subtask in the given scope. The subtask creates a new child scope with
179 * the given name, then parks. This method returns Thread that executes the subtask.
180 */
181 private static Thread fork(StructuredTaskScope<Object, Void> scope,
182 String childScopeName) throws Exception {
183 var ref = new AtomicReference<Thread>();
184 scope.fork(() -> {
185 try (var childScope = StructuredTaskScope.open(Joiner.awaitAll(),
186 cf -> cf.withName(childScopeName))) {
187 ref.set(Thread.currentThread());
188 LockSupport.park();
189 }
190 });
191 Thread thread;
192 while ((thread = ref.get()) == null) {
193 Thread.sleep(10);
194 }
195 return thread;
196 }
197
198 }
|