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 }