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 }