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