1 /*
  2  * Copyright (c) 2021, 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.concurrent.atomic.LongAdder;
 30 import java.util.function.Supplier;
 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 implements ThreadContainer, AutoCloseable {
 40     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 41     private static final VarHandle CLOSED;
 42     static {
 43         try {
 44             MethodHandles.Lookup l = MethodHandles.lookup();
 45             CLOSED = l.findVarHandle(SharedThreadContainer.class, "closed", boolean.class);
 46         } catch (Exception e) {
 47             throw new InternalError(e);
 48         }
 49     }
 50 
 51     private final String name;
 52     private final Supplier<Stream<Thread>> threadsSupplier;
 53     private final LongAdder threadCount;
 54     private final Object key;
 55     private volatile boolean closed;
 56 
 57     private SharedThreadContainer(String name, Supplier<Stream<Thread>> threadsSupplier) {
 58         this.name = name;
 59         if (threadsSupplier != null) {
 60             this.threadsSupplier = threadsSupplier;
 61             this.threadCount = null;
 62         } else {
 63             this.threadsSupplier = null;
 64             this.threadCount = new LongAdder();
 65         }
 66         this.key = ThreadContainers.registerSharedContainer(this);
 67     }
 68 
 69     /**
 70      * Creates a shared thread container with the given name.
 71      */
 72     public static SharedThreadContainer create(String name) {
 73         return new SharedThreadContainer(name, null);
 74     }
 75 
 76     /**
 77      * Creates a shared thread container with the given name and threads supplier.
 78      */
 79     public static SharedThreadContainer create(String name,
 80                                                Supplier<Stream<Thread>> threadsSupplier) {
 81         return new SharedThreadContainer(name, threadsSupplier);
 82     }
 83 
 84     @Override
 85     public String name() {
 86         return name;
 87     }
 88 
 89     @Override
 90     public void onStart(Thread thread) {
 91         if (threadCount != null) {
 92             threadCount.add(1L);
 93         }
 94     }
 95 
 96     @Override
 97     public void onExit(Thread thread) {
 98         if (threadCount != null) {
 99             threadCount.add(-1L);
100         }
101     }
102 
103     @Override
104     public long threadCount() {
105         if (threadCount != null) {
106             return threadCount.sum();
107         } else {
108             return threads().mapToLong(e -> 1L).sum();
109         }
110     }
111 
112     @Override
113     public Stream<Thread> threads() {
114         if (threadsSupplier != null) {
115             return threadsSupplier.get();
116         } else {
117             return Stream.of(JLA.getAllThreads())
118                     .filter(t -> JLA.threadContainer(t) == this);
119         }
120     }
121 
122     /**
123      * Starts a thread in this container.
124      * @throws IllegalStateException if the container is closed
125      */
126     public void start(Thread thread) {
127         if (closed)
128             throw new IllegalStateException();
129         JLA.start(thread, this);
130     }
131 
132     /**
133      * Closes this container. Further attempts to start a thread in this container
134      * throw IllegalStateException. This method has no impact on threads that are
135      * still running or starting around the time that this method is invoked.
136      */
137     public void close() {
138         if (!closed && CLOSED.compareAndSet(this, false, true)) {
139             ThreadContainers.deregisterSharedContainer(key);
140         }
141     }
142 
143     @Override
144     public String toString() {
145         String id = getClass().getName() + "@" + System.identityHashCode(this);
146         String name = name();
147         if (name != null) {
148             return name + "/" + id;
149         } else {
150             return id;
151         }
152     }
153 }