1 /* 2 * Copyright (c) 2021, 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. 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 jdk.internal.vm; 26 27 import java.lang.invoke.MethodHandles; 28 import java.lang.invoke.VarHandle; 29 import java.util.Set; 30 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.stream.Stream; 32 import jdk.internal.access.JavaLangAccess; 33 import jdk.internal.access.SharedSecrets; 34 35 /** 36 * A "shared" thread container. A shared thread container doesn't have an owner 37 * and is intended for unstructured uses, e.g. thread pools. 38 */ 39 public class SharedThreadContainer extends ThreadContainer implements AutoCloseable { 40 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 41 private static final VarHandle CLOSED; 42 private static final VarHandle VIRTUAL_THREADS; 43 static { 44 try { 45 MethodHandles.Lookup l = MethodHandles.lookup(); 46 CLOSED = l.findVarHandle(SharedThreadContainer.class, 47 "closed", boolean.class); 48 VIRTUAL_THREADS = l.findVarHandle(SharedThreadContainer.class, 49 "virtualThreads", Set.class); 50 } catch (Exception e) { 51 throw new ExceptionInInitializerError(e); 52 } 53 } 54 55 // name of container, used by toString 56 private final String name; 57 58 // the virtual threads in the container, created lazily 59 private volatile Set<Thread> virtualThreads; 60 61 // the key for this container in the registry 62 private volatile Object key; 63 64 // set to true when the container is closed 65 private volatile boolean closed; 66 67 /** 68 * Initialize a new SharedThreadContainer. 69 * @param name the container name, can be null 70 */ 71 private SharedThreadContainer(String name) { 72 super(/*shared*/ true); 73 this.name = name; 74 } 75 76 /** 77 * Creates a shared thread container with the given parent and name. 78 * @throws IllegalArgumentException if the parent has an owner. 79 */ 80 public static SharedThreadContainer create(ThreadContainer parent, String name) { 81 if (parent.owner() != null) 82 throw new IllegalArgumentException("parent has owner"); 83 var container = new SharedThreadContainer(name); 84 // register the container to allow discovery by serviceability tools 85 container.key = ThreadContainers.registerContainer(container); 86 return container; 87 } 88 89 /** 90 * Creates a shared thread container with the given name. Its parent will be 91 * the root thread container. 92 */ 93 public static SharedThreadContainer create(String name) { 94 return create(ThreadContainers.root(), name); 95 } 96 97 @Override 98 public String name() { 99 return name; 100 } 101 102 @Override 103 public Thread owner() { 104 return null; 105 } 106 107 @Override 108 public void onStart(Thread thread) { 109 // virtual threads needs to be tracked 110 if (thread.isVirtual()) { 111 Set<Thread> vthreads = this.virtualThreads; 112 if (vthreads == null) { 113 vthreads = ConcurrentHashMap.newKeySet(); 114 if (!VIRTUAL_THREADS.compareAndSet(this, null, vthreads)) { 115 // lost the race 116 vthreads = this.virtualThreads; 117 } 118 } 119 vthreads.add(thread); 120 } 121 } 122 123 @Override 124 public void onExit(Thread thread) { 125 if (thread.isVirtual()) 126 virtualThreads.remove(thread); 127 } 128 129 @Override 130 public Stream<Thread> threads() { 131 // live platform threads in this container 132 Stream<Thread> platformThreads = Stream.of(JLA.getAllThreads()) 133 .filter(t -> JLA.threadContainer(t) == this); 134 Set<Thread> vthreads = this.virtualThreads; 135 if (vthreads == null) { 136 // live platform threads only, no virtual threads 137 return platformThreads; 138 } else { 139 // all live threads in this container 140 return Stream.concat(platformThreads, 141 vthreads.stream().filter(Thread::isAlive)); 142 } 143 } 144 145 /** 146 * Starts a thread in this container. 147 * @throws IllegalStateException if the container is closed 148 */ 149 public void start(Thread thread) { 150 if (closed) 151 throw new IllegalStateException(); 152 JLA.start(thread, this); 153 } 154 155 /** 156 * Closes this container. Further attempts to start a thread in this container 157 * throw IllegalStateException. This method has no impact on threads that are 158 * still running or starting around the time that this method is invoked. 159 */ 160 @Override 161 public void close() { 162 if (!closed && CLOSED.compareAndSet(this, false, true)) { 163 ThreadContainers.deregisterContainer(key); 164 } 165 } 166 }