1 /*
  2  * Copyright (c) 2019, 2022, 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 jdk.internal.access.JavaLangAccess;
 30 import jdk.internal.access.JavaUtilConcurrentFJPAccess;
 31 import jdk.internal.access.SharedSecrets;
 32 
 33 /**
 34  * Defines static methods to mark the beginning and end of a possibly blocking
 35  * operation. The methods are intended to be used with try-finally as follows:
 36  * {@snippet lang=java :
 37  *     long comp = Blocker.begin();
 38  *     try {
 39  *         // blocking operation
 40  *     } finally {
 41  *         Blocker.end(comp);
 42  *     }
 43  * }
 44  * If invoked from a virtual thread and the underlying carrier thread is a
 45  * CarrierThread then the code in the block runs as if it were in run in
 46  * ForkJoinPool.ManagedBlocker. This means the pool can be expanded to support
 47  * additional parallelism during the blocking operation.
 48  */
 49 public class Blocker {
 50     private static final JavaLangAccess JLA;
 51     static {
 52         JLA = SharedSecrets.getJavaLangAccess();
 53         if (JLA == null) {
 54             throw new InternalError("JavaLangAccess not setup");
 55         }
 56     }
 57 
 58     private Blocker() { }
 59 
 60     private static Thread currentCarrierThread() {
 61         return JLA.currentCarrierThread();
 62     }
 63 
 64     /**
 65      * Marks the beginning of a possibly blocking operation.
 66      * @return the return value from the attempt to compensate or -1 if not attempted
 67      */
 68     public static long begin() {
 69         if (VM.isBooted()
 70                 && Thread.currentThread().isVirtual()
 71                 && currentCarrierThread() instanceof CarrierThread ct && !ct.inBlocking()) {
 72             ct.beginBlocking();
 73             boolean completed = false;
 74             try {
 75                 long comp = ForkJoinPools.beginCompensatedBlock(ct.getPool());
 76                 assert currentCarrierThread() == ct;
 77                 completed = true;
 78                 return comp;
 79             } finally {
 80                 if (!completed) {
 81                     ct.endBlocking();
 82                 }
 83             }
 84         }
 85         return -1;
 86     }
 87 
 88     /**
 89      * Marks the beginning of a possibly blocking operation.
 90      * @param blocking true if the operation may block, otherwise false
 91      * @return the return value from the attempt to compensate, -1 if not attempted
 92      * or blocking is false
 93      */
 94     public static long begin(boolean blocking) {
 95         return (blocking) ? begin() : -1;
 96     }
 97 
 98     /**
 99      * Marks the end of an operation that may have blocked.
100      * @param compensateReturn the value returned by the begin method
101      */
102     public static void end(long compensateReturn) {
103         if (compensateReturn >= 0) {
104             assert currentCarrierThread() instanceof CarrierThread ct && ct.inBlocking();
105             CarrierThread ct = (CarrierThread) currentCarrierThread();
106             ForkJoinPools.endCompensatedBlock(ct.getPool(), compensateReturn);
107             ct.endBlocking();
108         }
109     }
110 
111     /**
112      * Defines static methods to invoke non-public ForkJoinPool methods via the
113      * shared secret support.
114      */
115     private static class ForkJoinPools {
116         private static final JavaUtilConcurrentFJPAccess FJP_ACCESS =
117                 SharedSecrets.getJavaUtilConcurrentFJPAccess();
118         static long beginCompensatedBlock(ForkJoinPool pool) {
119             return FJP_ACCESS.beginCompensatedBlock(pool);
120         }
121         static void endCompensatedBlock(ForkJoinPool pool, long post) {
122             FJP_ACCESS.endCompensatedBlock(pool, post);
123         }
124     }
125 
126 }