1 /*
2 * Copyright (c) 2021, 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
26 package jdk.internal.misc;
27
28 import java.util.concurrent.ForkJoinPool;
29 import java.util.concurrent.ForkJoinWorkerThread;
30 import jdk.internal.access.JavaLangAccess;
31 import jdk.internal.access.JavaUtilConcurrentFJPAccess;
32 import jdk.internal.access.SharedSecrets;
33 import jdk.internal.vm.Continuation;
34
35 /**
36 * A ForkJoinWorkerThread that can be used as a carrier thread.
37 */
38 public class CarrierThread extends ForkJoinWorkerThread {
39 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
40 private static final Unsafe U = Unsafe.getUnsafe();
41
42 private static final ThreadGroup CARRIER_THREADGROUP = carrierThreadGroup();
43
44 private static final long CONTEXTCLASSLOADER;
45 private static final long INHERITABLETHREADLOCALS;
46
47 // compensating state
48 private static final int NOT_COMPENSATING = 0;
49 private static final int COMPENSATE_IN_PROGRESS = 1;
50 private static final int COMPENSATING = 2;
51 private int compensating;
52
53 // FJP value to adjust release counts
54 private long compensateValue;
55
56 @SuppressWarnings("this-escape")
57 public CarrierThread(ForkJoinPool pool) {
58 super(CARRIER_THREADGROUP, pool, true);
59 U.putReference(this, CONTEXTCLASSLOADER, ClassLoader.getSystemClassLoader());
60 U.putReference(this, INHERITABLETHREADLOCALS, null);
61 }
62
63 /**
64 * Mark the start of a blocking operation.
65 */
66 public boolean beginBlocking() {
67 assert Thread.currentThread().isVirtual() && JLA.currentCarrierThread() == this;
68
69 if (compensating == NOT_COMPENSATING) {
70 // don't preempt when attempting to compensate
71 Continuation.pin();
72 try {
73 compensating = COMPENSATE_IN_PROGRESS;
74
75 // Uses FJP.tryCompensate to start or re-activate a spare thread
76 compensateValue = ForkJoinPools.beginCompensatedBlock(getPool());
77 compensating = COMPENSATING;
78 return true;
79 } catch (Throwable e) {
80 // exception starting spare thread
81 compensating = NOT_COMPENSATING;
82 throw e;
83 } finally {
84 Continuation.unpin();
85 }
86 } else {
87 return false;
88 }
89 }
90
91 /**
92 * Mark the end of a blocking operation.
93 */
94 public void endBlocking() {
95 assert Thread.currentThread() == this || JLA.currentCarrierThread() == this;
96 if (compensating == COMPENSATING) {
97 ForkJoinPools.endCompensatedBlock(getPool(), compensateValue);
98 compensating = NOT_COMPENSATING;
99 compensateValue = 0;
100 }
101 }
102
103 @Override
104 public void setUncaughtExceptionHandler(UncaughtExceptionHandler ueh) { }
105
106 @Override
107 public void setContextClassLoader(ClassLoader cl) {
108 throw new SecurityException("setContextClassLoader");
109 }
110
111 /**
112 * The thread group for the carrier threads.
113 */
114 private static ThreadGroup carrierThreadGroup() {
115 ThreadGroup group = JLA.currentCarrierThread().getThreadGroup();
116 for (ThreadGroup p; (p = group.getParent()) != null; )
117 group = p;
118 var carrierThreadsGroup = new ThreadGroup(group, "CarrierThreads");
119 return carrierThreadsGroup;
120 }
121
122 /**
123 * Defines static methods to invoke non-public ForkJoinPool methods via the
124 * shared secret support.
125 */
126 private static class ForkJoinPools {
127 private static final JavaUtilConcurrentFJPAccess FJP_ACCESS =
128 SharedSecrets.getJavaUtilConcurrentFJPAccess();
129 static long beginCompensatedBlock(ForkJoinPool pool) {
130 return FJP_ACCESS.beginCompensatedBlock(pool);
131 }
132 static void endCompensatedBlock(ForkJoinPool pool, long post) {
133 FJP_ACCESS.endCompensatedBlock(pool, post);
134 }
135 }
136
137 static {
138 CONTEXTCLASSLOADER = U.objectFieldOffset(Thread.class,
139 "contextClassLoader");
140 INHERITABLETHREADLOCALS = U.objectFieldOffset(Thread.class,
141 "inheritableThreadLocals");
142 }
143 }