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