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