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 }