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 }