1 /* 2 * Copyright (c) 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.management.internal; 26 27 import java.lang.reflect.InvocationTargetException; 28 import java.lang.reflect.Method; 29 import java.util.concurrent.Executor; 30 import java.util.concurrent.ForkJoinPool; 31 import javax.management.ObjectName; 32 import jdk.management.VirtualThreadSchedulerMXBean; 33 import jdk.internal.access.JavaLangAccess; 34 import jdk.internal.access.SharedSecrets; 35 import jdk.internal.vm.ContinuationSupport; 36 import sun.management.Util; 37 38 /** 39 * Provides the implementation of the management interface for the JDK's default virtual 40 * thread scheduler. 41 */ 42 public class VirtualThreadSchedulerImpls { 43 private VirtualThreadSchedulerImpls() { 44 } 45 46 public static VirtualThreadSchedulerMXBean create() { 47 if (ContinuationSupport.isSupported()) { 48 return new VirtualThreadSchedulerImpl(); 49 } else { 50 return new BoundVirtualThreadSchedulerImpl(); 51 } 52 } 53 54 /** 55 * Base implementation of VirtualThreadSchedulerMXBean. 56 */ 57 private abstract static class BaseVirtualThreadSchedulerImpl 58 implements VirtualThreadSchedulerMXBean { 59 60 @Override 61 public final ObjectName getObjectName() { 62 return Util.newObjectName("jdk.management:type=VirtualThreadScheduler"); 63 } 64 65 @Override 66 public String toString() { 67 var sb = new StringBuilder("[parallelism="); 68 sb.append(getParallelism()); 69 append(sb, "size", getPoolSize()); 70 append(sb, "mounted", getMountedVirtualThreadCount()); 71 append(sb, "queued", getQueuedVirtualThreadCount()); 72 sb.append(']'); 73 return sb.toString(); 74 } 75 76 private void append(StringBuilder sb, String name, long value) { 77 sb.append(", ").append(name).append('='); 78 if (value >= 0) { 79 sb.append(value); 80 } else { 81 sb.append("<unavailable>"); 82 } 83 } 84 } 85 86 /** 87 * Implementation of VirtualThreadSchedulerMXBean when virtual threads are 88 * implemented with continuations + scheduler. 89 */ 90 private static final class VirtualThreadSchedulerImpl extends BaseVirtualThreadSchedulerImpl { 91 /** 92 * Holder class for scheduler. 93 */ 94 private static class Scheduler { 95 private static final Executor SCHEDULER = 96 SharedSecrets.getJavaLangAccess().virtualThreadDefaultScheduler(); 97 static Executor instance() { 98 return SCHEDULER; 99 } 100 } 101 102 /** 103 * Reflective access for custom schedulers. 104 */ 105 private static class SchedulerMethods { 106 private final static Method GET_PARALLELISM = findMethod("getParallelism"); 107 private final static Method SET_PARALLELISM = findMethod("setParallelism", int.class); 108 private final static Method GET_POOL_SIZE = findMethod("getPoolSize"); 109 private final static Method GET_MOUNTED_VTHREAD_COUNT = findMethod("getMountedVirtualThreadCount"); 110 private final static Method GET_QUEUED_VTHREAD_COUNT = findMethod("getQueuedVirtualThreadCount"); 111 112 static Method findMethod(String method, Class<?>... params) { 113 try { 114 return Scheduler.instance().getClass().getMethod(method, params); 115 } catch (Exception e) { 116 return null; 117 } 118 } 119 } 120 121 @Override 122 public int getParallelism() { 123 Executor scheduler = Scheduler.instance(); 124 if (scheduler instanceof ForkJoinPool pool) { 125 return pool.getParallelism(); 126 } 127 128 // custom scheduler 129 if (SchedulerMethods.GET_PARALLELISM instanceof Method m) { 130 return (int) invokeSchedulerMethod(m); 131 } 132 133 return -1; // unknown 134 } 135 136 @Override 137 public void setParallelism(int size) {Executor scheduler = Scheduler.instance(); 138 if (scheduler instanceof ForkJoinPool pool) { 139 pool.setParallelism(size); 140 if (pool.getPoolSize() < size) { 141 // FJ worker thread creation is on-demand 142 Thread.startVirtualThread(() -> { }); 143 } 144 return; 145 } 146 147 // custom scheduler 148 if (SchedulerMethods.SET_PARALLELISM instanceof Method m) { 149 invokeSchedulerMethod(m, size); 150 } 151 152 throw new UnsupportedOperationException(); 153 } 154 155 @Override 156 public int getPoolSize() { 157 Executor scheduler = Scheduler.instance(); 158 if (scheduler instanceof ForkJoinPool pool) { 159 return pool.getPoolSize(); 160 } 161 162 // custom scheduler 163 if (SchedulerMethods.GET_POOL_SIZE instanceof Method m) { 164 return (int) invokeSchedulerMethod(m); 165 } 166 167 return -1; // unknown 168 } 169 170 @Override 171 public int getMountedVirtualThreadCount() { 172 Executor scheduler = Scheduler.instance(); 173 if (scheduler instanceof ForkJoinPool pool) { 174 return pool.getActiveThreadCount(); 175 } 176 177 // custom scheduler 178 if (SchedulerMethods.GET_MOUNTED_VTHREAD_COUNT instanceof Method m) { 179 return (int) invokeSchedulerMethod(m); 180 } 181 182 return -1; // unknown 183 } 184 185 @Override 186 public long getQueuedVirtualThreadCount() { 187 Executor scheduler = Scheduler.instance(); 188 if (scheduler instanceof ForkJoinPool pool) { 189 return pool.getQueuedTaskCount() + pool.getQueuedSubmissionCount(); 190 } 191 192 // custom scheduler 193 if (SchedulerMethods.GET_QUEUED_VTHREAD_COUNT instanceof Method m) { 194 return (long) invokeSchedulerMethod(m); 195 } 196 197 return -1L; // unknown 198 } 199 200 private static Object invokeSchedulerMethod(Method m, Object... args) { 201 try { 202 return m.invoke(Scheduler.instance(), args); 203 } catch (InvocationTargetException e) { 204 throw new RuntimeException(e.getCause()); 205 } catch (IllegalAccessException e) { 206 throw new RuntimeException(e); 207 } 208 } 209 } 210 211 /** 212 * Implementation of VirtualThreadSchedulerMXBean when virtual threads are backed 213 * by platform threads. 214 */ 215 private static final class BoundVirtualThreadSchedulerImpl extends BaseVirtualThreadSchedulerImpl { 216 @Override 217 public int getParallelism() { 218 return Integer.MAX_VALUE; 219 } 220 221 @Override 222 public void setParallelism(int size) { 223 throw new UnsupportedOperationException(); 224 } 225 226 @Override 227 public int getPoolSize() { 228 return -1; 229 } 230 231 @Override 232 public int getMountedVirtualThreadCount() { 233 return -1; 234 } 235 236 @Override 237 public long getQueuedVirtualThreadCount() { 238 return -1L; 239 } 240 } 241 } 242