1 /*
  2  * Copyright (c) 2021, 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 package jdk.internal.vm;
 26 
 27 import java.util.concurrent.Callable;
 28 import jdk.internal.access.JavaLangAccess;
 29 import jdk.internal.access.SharedSecrets;
 30 import jdk.internal.misc.StructureViolationExceptions;
 31 import jdk.internal.misc.Unsafe;
 32 import jdk.internal.vm.annotation.DontInline;
 33 import jdk.internal.vm.annotation.ReservedStackAccess;
 34 
 35 /**
 36  * A StackableScope to represent extent-local bindings.
 37  *
 38  * This class defines static methods to run an operation with a ExtentLocalContainer
 39  * on the scope stack. It also defines a method to get the latest ExtentLocalContainer
 40  * and a method to return a snapshot of the extent local bindings.
 41  */
 42 public class ExtentLocalContainer extends StackableScope {
 43     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 44     static {
 45         Unsafe.getUnsafe().ensureClassInitialized(StructureViolationExceptions.class);
 46     }
 47 
 48     private ExtentLocalContainer() {
 49     }
 50 
 51     /**
 52      * Returns the "latest" ExtentLocalContainer for the current Thread. This may be on
 53      * the current thread's scope task or ma require walking up the tree to find it.
 54      */
 55     public static <T extends ExtentLocalContainer> T latest(Class<T> containerClass) {
 56         StackableScope scope = head();
 57         if (scope == null) {
 58             scope = JLA.threadContainer(Thread.currentThread());
 59             if (scope == null || scope.owner() == null)
 60                 return null;
 61         }
 62         if (containerClass.isInstance(scope)) {
 63             @SuppressWarnings("unchecked")
 64             T tmp = (T) scope;
 65             return tmp;
 66         } else {
 67             return scope.enclosingScope(containerClass);
 68         }
 69     }
 70 
 71     /**
 72      * Returns the "latest" ExtentLocalContainer for the current Thread. This
 73      * may be on the current thread's scope task or may require walking up the
 74      * tree to find it.
 75      */
 76     public static ExtentLocalContainer latest() {
 77         return latest(ExtentLocalContainer.class);
 78     }
 79 
 80     /**
 81      * A snapshot of the extent local bindings. The snapshot includes the bindings
 82      * established for the current thread and extent local container.
 83      */
 84     public record BindingsSnapshot(Object extentLocalBindings,
 85                                    ExtentLocalContainer container) { }
 86 
 87     /**
 88      * Returns the extent local bindings for the current thread.
 89      */
 90     public static BindingsSnapshot captureBindings() {
 91         return new BindingsSnapshot(JLA.extentLocalBindings(), latest());
 92     }
 93 
 94     /**
 95      * For use by ExtentLocal to run an operation in a structured context.
 96      */
 97     public static void run(Runnable op) {
 98         if (head() == null) {
 99             // no need to push scope when stack is empty
100             runWithoutScope(op);
101         } else {
102             new ExtentLocalContainer().doRun(op);
103         }
104     }
105 
106     /**
107      * Run an operation without a scope on the stack.
108      */
109     private static void runWithoutScope(Runnable op) {
110         assert head() == null;
111         Throwable ex;
112         boolean atTop;
113         try {
114             op.run();
115             ex = null;
116         } catch (Throwable e) {
117             ex = e;
118         } finally {
119             atTop = (head() == null);
120             if (!atTop) popAll();   // may block
121         }
122         throwIfFailed(ex, atTop);
123     }
124 
125     /**
126      * Run an operation with this scope on the stack.
127      */
128     private void doRun(Runnable op) {
129         Throwable ex;
130         boolean atTop;
131         push();
132         try {
133             op.run();
134             ex = null;
135         } catch (Throwable e) {
136             ex = e;
137         } finally {
138             atTop = popForcefully();  // may block
139         }
140         throwIfFailed(ex, atTop);
141     }
142 
143     /**
144      * For use by ExtentLocal to call a value returning operation in a structured context.
145      */
146     public static <V> V call(Callable<V> op) throws Exception {
147         if (head() == null) {
148             // no need to push scope when stack is empty
149             return callWithoutScope(op);
150         } else {
151             return new ExtentLocalContainer().doCall(op);
152         }
153     }
154 
155     /**
156      * Call an operation without a scope on the stack.
157      */
158     private static <V> V callWithoutScope(Callable<V> op) {
159         assert head() == null;
160         Throwable ex;
161         boolean atTop;
162         V result;
163         try {
164             result = op.call();
165             ex = null;
166         } catch (Throwable e) {
167             result = null;
168             ex = e;
169         } finally {
170             atTop = (head() == null);
171             if (!atTop) popAll();  // may block
172         }
173         throwIfFailed(ex, atTop);
174         return result;
175     }
176 
177     /**
178      * Call an operation with this scope on the stack.
179      */
180     private <V> V doCall(Callable<V> op) {
181         Throwable ex;
182         boolean atTop;
183         V result;
184         push();
185         try {
186             result = op.call();
187             ex = null;
188         } catch (Throwable e) {
189             result = null;
190             ex = e;
191         } finally {
192             atTop = popForcefully();  // may block
193         }
194         throwIfFailed(ex, atTop);
195         return result;
196     }
197 
198     /**
199      * Throws {@code ex} if not null. StructureViolationException is thrown or added
200      * as a suppressed exception when {@code atTop} is false.
201      */
202     @DontInline @ReservedStackAccess
203     private static void throwIfFailed(Throwable ex, boolean atTop) {
204         if (ex != null || !atTop) {
205             if (!atTop) {
206                 var sve = StructureViolationExceptions.newException();
207                 if (ex == null) {
208                     ex = sve;
209                 } else {
210                     ex.addSuppressed(sve);
211                 }
212             }
213             Unsafe.getUnsafe().throwException(ex);
214         }
215     }
216 }