1 /* 2 * Copyright (c) 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 * @summary Basic test com.sun.management.Threads.currentThreadEnclosingScopes 27 * @enablePreview 28 * @run junit CurrentThreadEnclosingScopes 29 */ 30 31 import java.lang.management.ManagementFactory; 32 import java.util.concurrent.Executors; 33 import java.util.concurrent.StructuredTaskScope.ShutdownOnFailure; 34 import java.util.concurrent.ThreadFactory; 35 import com.sun.management.Threads; 36 37 import org.junit.jupiter.api.Test; 38 import static org.junit.jupiter.api.Assertions.*; 39 40 class CurrentThreadEnclosingScopes { 41 42 /** 43 * Test thread in "root" container. 44 */ 45 @Test 46 void testRootContainer() { 47 String s = Threads.currentThreadEnclosingScopes(); 48 assertTrue(s.isEmpty()); 49 } 50 51 /** 52 * Test thread started in executor service. 53 */ 54 @Test 55 void testExecutorService() throws Exception { 56 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { 57 executor.submit(() -> { 58 String s = Threads.currentThreadEnclosingScopes(); 59 assertTrue(s.isEmpty()); 60 }).get(); 61 } 62 } 63 64 /** 65 * Test thread running in StructuredTaskScope, scope owned by main thread. 66 */ 67 @Test 68 void testEnclosingScope() throws Exception { 69 Thread mainThread = Thread.currentThread(); 70 try (var scope = new ShutdownOnFailure("duke", Thread.ofVirtual().factory())) { 71 scope.fork(() -> { 72 // wait for main thread to block so its stack trace is stable 73 while (mainThread.getState() != Thread.State.WAITING) { 74 Thread.sleep(10); 75 } 76 77 // enclosing scope should include the main thread (as owner) 78 String s = Threads.currentThreadEnclosingScopes(); 79 80 // scope name and owner expected on the first line 81 String line1 = s.lines().findFirst().orElseThrow(); 82 assertTrue(line1.contains("duke")); 83 assertTrue(line1.contains(mainThread.toString())); 84 85 // stack trace of owner should be in the string 86 for (StackTraceElement e : mainThread.getStackTrace()) { 87 assertTrue(s.contains(e.toString())); 88 } 89 90 return null; 91 }); 92 scope.join(); 93 scope.throwIfFailed(); 94 } 95 } 96 97 /** 98 * Test thread running in StructuredTaskScope, outer and inner scopes owned by main 99 * thread. 100 */ 101 @Test 102 void testNestedEnclosingScopes1() throws Exception { 103 Thread mainThread = Thread.currentThread(); 104 ThreadFactory factory = Thread.ofVirtual().factory(); 105 try (var scope1 = new ShutdownOnFailure("duke-outer", factory)) { 106 try (var scope2 = new ShutdownOnFailure("duke-inner", factory)) { 107 scope2.fork(() -> { 108 // wait for main thread to block so its stack trace is stable 109 while (mainThread.getState() != Thread.State.WAITING) { 110 Thread.sleep(10); 111 } 112 113 // enclosing scopes 114 String s = Threads.currentThreadEnclosingScopes(); 115 116 // scope name and name on first line 117 String line1 = s.lines().findFirst().orElseThrow(); 118 assertTrue(line1.contains("duke-inner")); 119 assertTrue(line1.contains(mainThread.toString())); 120 121 // string should contain name of outer scope 122 assertTrue(s.contains("duke-outer")); 123 124 // stack trace of owner should be in the string 125 for (StackTraceElement e : mainThread.getStackTrace()) { 126 assertTrue(s.contains(e.toString())); 127 } 128 return null; 129 }); 130 scope2.join(); 131 scope2.throwIfFailed(); 132 } 133 scope1.join(); 134 } 135 } 136 137 /** 138 * Test thread running in StructuredTaskScope, outer scope owned by main thread, 139 * inner scope owned by child thread. 140 */ 141 @Test 142 void testNestedEnclosingScopes2() throws Exception { 143 Thread mainThread = Thread.currentThread(); 144 ThreadFactory factory = Thread.ofVirtual().factory(); 145 try (var scope1 = new ShutdownOnFailure("duke-outer", factory)) { 146 scope1.fork(() -> { 147 Thread childThread = Thread.currentThread(); 148 try (var scope2 = new ShutdownOnFailure("duke-inner", factory)) { 149 scope2.fork(() -> { 150 // wait for threads to blocks so stack traces are stable 151 while (mainThread.getState() != Thread.State.WAITING 152 || childThread.getState() != Thread.State.WAITING) { 153 Thread.sleep(10); 154 } 155 156 // enclosing scopes 157 String s = Threads.currentThreadEnclosingScopes(); 158 159 // scope name and name on first line 160 String line1 = s.lines().findFirst().orElseThrow(); 161 assertTrue(line1.contains("duke-inner")); 162 assertTrue(line1.contains(childThread.toString())); 163 164 // string should contain name of outer scope 165 assertTrue(s.contains("duke-outer")); 166 167 // stack trace of owners should be in the string 168 for (StackTraceElement e : mainThread.getStackTrace()) { 169 assertTrue(s.contains(e.toString())); 170 } 171 for (StackTraceElement e : childThread.getStackTrace()) { 172 assertTrue(s.contains(e.toString())); 173 } 174 175 return null; 176 }); 177 scope2.join(); 178 scope2.throwIfFailed(); 179 } 180 return null; 181 }); 182 scope1.join(); 183 scope1.throwIfFailed(); 184 } 185 } 186 }