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