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                 && currentCarrierThread() instanceof CarrierThread ct && !ct.inBlocking()) {
 71             ct.beginBlocking();
 72             boolean completed = false;
 73             try {
 74                 long comp = ForkJoinPools.beginCompensatedBlock(ct.getPool());
 75                 assert currentCarrierThread() == ct;
 76                 completed = true;
 77                 return comp;
 78             } finally {
 79                 if (!completed) {
 80                     ct.endBlocking();
 81                 }
 82             }
 83         }
 84         return -1;
 85     }
 86 
 87     /**
 88      * Marks the beginning of a possibly blocking operation.
 89      * @param blocking true if the operation may block, otherwise false
 90      * @return the return value from the attempt to compensate, -1 if not attempted
 91      * or blocking is false
 92      */
 93     public static long begin(boolean blocking) {
 94         return (blocking) ? begin() : -1;
 95     }
 96 
 97     /**
 98      * Marks the end of an operation that may have blocked.
 99      * @param compensateReturn the value returned by the begin method
100      */
101     public static void end(long compensateReturn) {
102         if (compensateReturn >= 0) {
103             assert currentCarrierThread() instanceof CarrierThread ct && ct.inBlocking();
104             CarrierThread ct = (CarrierThread) currentCarrierThread();
105             ForkJoinPools.endCompensatedBlock(ct.getPool(), compensateReturn);
106             ct.endBlocking();
107         }
108     }
109 
110     /**
111      * Defines static methods to invoke non-public ForkJoinPool methods via the
112      * shared secret support.
113      */
114     private static class ForkJoinPools {
115         private static final JavaUtilConcurrentFJPAccess FJP_ACCESS =
116                 SharedSecrets.getJavaUtilConcurrentFJPAccess();
117         static long beginCompensatedBlock(ForkJoinPool pool) {
118             return FJP_ACCESS.beginCompensatedBlock(pool);
119         }
120         static void endCompensatedBlock(ForkJoinPool pool, long post) {
121             FJP_ACCESS.endCompensatedBlock(pool, post);
122         }
123     }
124 
125 }