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 }