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 assert compensating == NOT_COMPENSATING || compensating == COMPENSATING;
69
70 if (compensating == NOT_COMPENSATING) {
71 // don't preempt when attempting to compensate
72 Continuation.pin();
73 try {
74 compensating = COMPENSATE_IN_PROGRESS;
75
76 // Uses FJP.tryCompensate to start or re-activate a spare thread
77 compensateValue = ForkJoinPools.beginCompensatedBlock(getPool());
78 compensating = COMPENSATING;
79 return true;
80 } catch (Throwable e) {
81 // exception starting spare thread
82 compensating = NOT_COMPENSATING;
83 throw e;
84 } finally {
85 Continuation.unpin();
86 }
87 } else {
88 return false;
89 }
90 }
91
92 /**
93 * Mark the end of a blocking operation.
94 */
95 public void endBlocking() {
96 assert Thread.currentThread() == this || JLA.currentCarrierThread() == this;
97 if (compensating == COMPENSATING) {
98 ForkJoinPools.endCompensatedBlock(getPool(), compensateValue);
99 compensating = NOT_COMPENSATING;
100 compensateValue = 0;
101 }
102 }
103
104 @Override
105 public void setUncaughtExceptionHandler(UncaughtExceptionHandler ueh) { }
106
107 @Override
108 public void setContextClassLoader(ClassLoader cl) {
109 throw new SecurityException("setContextClassLoader");
110 }
111
112 /**
113 * The thread group for the carrier threads.
114 */
115 private static ThreadGroup carrierThreadGroup() {
116 ThreadGroup group = JLA.currentCarrierThread().getThreadGroup();
117 for (ThreadGroup p; (p = group.getParent()) != null; )
118 group = p;
119 var carrierThreadsGroup = new ThreadGroup(group, "CarrierThreads");
120 return carrierThreadsGroup;
121 }
122
123 /**
124 * Defines static methods to invoke non-public ForkJoinPool methods via the
125 * shared secret support.
126 */
127 private static class ForkJoinPools {
128 private static final JavaUtilConcurrentFJPAccess FJP_ACCESS =
129 SharedSecrets.getJavaUtilConcurrentFJPAccess();
130 static long beginCompensatedBlock(ForkJoinPool pool) {
131 return FJP_ACCESS.beginCompensatedBlock(pool);
132 }
133 static void endCompensatedBlock(ForkJoinPool pool, long post) {
134 FJP_ACCESS.endCompensatedBlock(pool, post);
135 }
136 }
137
138 static {
139 CONTEXTCLASSLOADER = U.objectFieldOffset(Thread.class,
140 "contextClassLoader");
141 INHERITABLETHREADLOCALS = U.objectFieldOffset(Thread.class,
142 "inheritableThreadLocals");
143 }
144 }