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.util.concurrent.ForkJoinPool;
 28 import java.util.concurrent.ThreadPoolExecutor;
 29 import javax.management.ObjectName;
 30 import jdk.management.VirtualThreadSchedulerMXBean;
 31 import jdk.internal.access.JavaLangAccess;
 32 import jdk.internal.access.SharedSecrets;
 33 import jdk.internal.vm.ContinuationSupport;
 34 import sun.management.Util;
 35 
 36 /**
 37  * Provides the implementation of the management interface for the JDK's virtual thread scheduler.
 38  */
 39 public class VirtualThreadSchedulerImpls {
 40     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 41 
 42     private VirtualThreadSchedulerImpls() {
 43     }
 44 
 45     /**
 46      * Creates the VirtualThreadSchedulerMXBean.
 47      */
 48     public static VirtualThreadSchedulerMXBean create() {
 49         // -XX:-VMContinuations
 50         if (!ContinuationSupport.isSupported()) {
 51             return new BoundVirtualThreadSchedulerImpl();
 52         }
 53 
 54         // built-in scheduler
 55         if (System.getProperty("jdk.virtualThreadScheduler.implClass") == null) {
 56             return new BuiltinVirtualThreadSchedulerImpl();
 57         }
 58 
 59         // custom scheduler implements VirtualThreadSchedulerMXBean
 60         if (JLA.defaultVirtualThreadScheduler() instanceof VirtualThreadSchedulerMXBean bean) {
 61             return bean;
 62         }
 63 
 64         // custom scheduler does not implement VirtualThreadSchedulerMXBean
 65         return new CustomVirtualThreadSchedulerImpl();
 66     }
 67 
 68     /**
 69      * Base implementation of VirtualThreadSchedulerMXBean.
 70      */
 71     private abstract static class BaseVirtualThreadSchedulerImpl
 72             implements VirtualThreadSchedulerMXBean {
 73 
 74         @Override
 75         public final ObjectName getObjectName() {
 76             return Util.newObjectName("jdk.management:type=VirtualThreadScheduler");
 77         }
 78 
 79         @Override
 80         public String toString() {
 81             var sb = new StringBuilder("[parallelism=");
 82             sb.append(getParallelism());
 83             append(sb, "size", getPoolSize());
 84             append(sb, "mounted", getMountedVirtualThreadCount());
 85             append(sb, "queued", getQueuedVirtualThreadCount());
 86             sb.append(']');
 87             return sb.toString();
 88         }
 89 
 90         private void append(StringBuilder sb, String name, long value) {
 91             sb.append(", ").append(name).append('=');
 92             if (value >= 0) {
 93                 sb.append(value);
 94             } else {
 95                 sb.append("<unavailable>");
 96             }
 97         }
 98     }
 99 
100     /**
101      * Implementation of VirtualThreadSchedulerMXBean when virtual threads are
102      * implemented with continuations and the built-in scheduler.
103      */
104     private static final class BuiltinVirtualThreadSchedulerImpl
105             extends BaseVirtualThreadSchedulerImpl {
106 
107         private Thread.VirtualThreadScheduler builtinScheduler() {
108             return JLA.builtinVirtualThreadScheduler();
109         }
110 
111         @Override
112         public int getParallelism() {
113             return switch (builtinScheduler()) {
114                 case ForkJoinPool pool -> pool.getParallelism();
115                 case ThreadPoolExecutor pool -> pool.getMaximumPoolSize();
116                 default -> -1;
117             };
118         }
119 
120         @Override
121         public void setParallelism(int size) {
122             if (builtinScheduler() instanceof ForkJoinPool pool) {
123                 pool.setParallelism(size);
124             } else {
125                 throw new UnsupportedOperationException();
126             }
127         }
128 
129         @Override
130         public int getPoolSize() {
131             return switch (builtinScheduler()) {
132                 case ForkJoinPool pool -> pool.getPoolSize();
133                 case ThreadPoolExecutor pool -> pool.getPoolSize();
134                 default -> -1;
135             };
136         }
137 
138         @Override
139         public int getMountedVirtualThreadCount() {
140             return switch (builtinScheduler()) {
141                 case ForkJoinPool pool -> pool.getActiveThreadCount();
142                 case ThreadPoolExecutor pool -> pool.getActiveCount();
143                 default -> -1;
144             };
145         }
146 
147         @Override
148         public long getQueuedVirtualThreadCount() {
149             return switch (builtinScheduler()) {
150                 case ForkJoinPool pool -> pool.getQueuedTaskCount() + pool.getQueuedSubmissionCount();
151                 case ThreadPoolExecutor pool -> pool.getQueue().size();
152                 default -> -1L;
153             };
154         }
155     }
156 
157     /**
158      * Implementation of VirtualThreadSchedulerMXBean when virtual threads are backed
159      * by platform threads.
160      */
161     private static final class BoundVirtualThreadSchedulerImpl
162             extends BaseVirtualThreadSchedulerImpl {
163         @Override
164         public int getParallelism() {
165             return Integer.MAX_VALUE;
166         }
167 
168         @Override
169         public void setParallelism(int size) {
170             throw new UnsupportedOperationException();
171         }
172 
173         @Override
174         public int getPoolSize() {
175             return -1;
176         }
177 
178         @Override
179         public int getMountedVirtualThreadCount() {
180             return -1;
181         }
182 
183         @Override
184         public long getQueuedVirtualThreadCount() {
185             return -1L;
186         }
187     }
188 
189     /**
190      * Implementation of VirtualThreadSchedulerMXBean when using a custom virtual
191      * thread scheduler that does not implement VirtualThreadSchedulerMXBean.
192      */
193     private static final class CustomVirtualThreadSchedulerImpl
194             extends BaseVirtualThreadSchedulerImpl {
195 
196         @Override
197         public int getParallelism() {
198             return 1;
199         }
200 
201         @Override
202         public void setParallelism(int size) {
203             throw new UnsupportedOperationException();
204         }
205 
206         @Override
207         public int getPoolSize() {
208             return -1;
209         }
210 
211         @Override
212         public int getMountedVirtualThreadCount() {
213             return -1;
214         }
215 
216         @Override
217         public long getQueuedVirtualThreadCount() {
218             return -1L;
219         }
220     }
221 }
222