1 /* 2 * Copyright (c) 2023, 2025, 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 8337199 27 * @summary Basic test for jcmd Thread.vthread_scheduler and Thread.vthread_pollers 28 * @requires vm.continuations 29 * @modules jdk.jcmd 30 * @library /test/lib 31 * @run junit/othervm VThreadCommandsTest 32 */ 33 34 /* 35 * @test id=poller-modes 36 * @requires (os.family == "linux") | (os.family == "mac") 37 * @requires vm.continuations 38 * @modules jdk.jcmd 39 * @library /test/lib 40 * @run junit/othervm -Djdk.pollerMode=1 VThreadCommandsTest 41 * @run junit/othervm -Djdk.pollerMode=2 VThreadCommandsTest 42 * @run junit/othervm -Djdk.pollerMode=3 VThreadCommandsTest 43 */ 44 45 import java.net.InetAddress; 46 import java.net.InetSocketAddress; 47 import java.net.ServerSocket; 48 import java.net.Socket; 49 import java.net.SocketTimeoutException; 50 import java.util.List; 51 import java.util.Objects; 52 import java.util.concurrent.Executors; 53 import java.util.concurrent.ExecutorService; 54 import java.util.concurrent.ForkJoinPool; 55 import java.util.concurrent.ForkJoinWorkerThread; 56 import java.util.concurrent.ScheduledThreadPoolExecutor; 57 import java.util.concurrent.atomic.AtomicBoolean; 58 import java.lang.management.ManagementFactory; 59 import jdk.management.VirtualThreadSchedulerMXBean; 60 61 import jdk.test.lib.dcmd.PidJcmdExecutor; 62 import jdk.test.lib.process.OutputAnalyzer; 63 import org.junit.jupiter.api.Test; 64 import static org.junit.jupiter.api.Assertions.*; 65 66 class VThreadCommandsTest { 67 68 /** 69 * Thread.vthread_scheduler 70 */ 71 @Test 72 void testVThreadScheduler() { 73 // ensure default scheduler is initialized 74 Thread.startVirtualThread(() -> { }); 75 76 jcmd("Thread.vthread_scheduler") 77 .shouldContain(Objects.toIdentityString(defaultScheduler())); 78 } 79 80 /** 81 * Thread.vthread_pollers 82 */ 83 @Test 84 void testVThreadPollers() throws Exception { 85 // do blocking I/O op on a virtual thread to ensure poller mechanism is initialized 86 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { 87 executor.submit(() -> { 88 try (var listener = new ServerSocket()) { 89 InetAddress lb = InetAddress.getLoopbackAddress(); 90 listener.bind(new InetSocketAddress(lb, 0)); 91 listener.setSoTimeout(200); 92 try (Socket s = listener.accept()) { 93 System.err.format("Connection from %s ??%n", s.getRemoteSocketAddress()); 94 } catch (SocketTimeoutException e) { 95 // expected 96 } 97 } 98 return null; 99 }).get(); 100 } 101 102 jcmd("Thread.vthread_pollers") 103 .shouldContain("Read I/O pollers:") 104 .shouldContain("Write I/O pollers:") 105 .shouldMatch("^\\[0\\] sun\\.nio\\.ch\\..+ \\[registered = [\\d]+, owner = .+\\]$"); 106 } 107 108 private OutputAnalyzer jcmd(String cmd) { 109 return new PidJcmdExecutor().execute(cmd); 110 } 111 112 /** 113 * Returns the virtual thread default scheduler. This implementation works by finding 114 * all FJ worker threads and mapping them to their pool. VirtualThreadSchedulerMXBean 115 * is used to temporarily changing target parallelism to an "unique" value, make it 116 * possbile to find the right pool. 117 */ 118 private ForkJoinPool defaultScheduler() { 119 var done = new AtomicBoolean(); 120 Thread vthread = Thread.startVirtualThread(() -> { 121 while (!done.get()) { 122 Thread.onSpinWait(); 123 } 124 }); 125 var bean = ManagementFactory.getPlatformMXBean(VirtualThreadSchedulerMXBean.class); 126 int parallelism = bean.getParallelism(); 127 try { 128 bean.setParallelism(133); 129 return Thread.getAllStackTraces() 130 .keySet() 131 .stream() 132 .filter(ForkJoinWorkerThread.class::isInstance) 133 .map(t -> ((ForkJoinWorkerThread) t).getPool()) 134 .filter(p -> p.getParallelism() == 133) 135 .findAny() 136 .orElseThrow(); 137 } finally { 138 bean.setParallelism(parallelism); 139 done.set(true); 140 } 141 } 142 }