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 jdk.internal.access.JavaLangAccess;
 28 import jdk.internal.access.SharedSecrets;
 29 
 30 /**
 31  * A stackable scope.
 32  */
 33 public class StackableScope {
 34     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 35 
 36     private final Thread owner;
 37     private volatile StackableScope previous;
 38 
 39     /**
 40      * Creates a stackable scope.
 41      */
 42     StackableScope(boolean shared) {
 43         if (shared) {
 44             this.owner = null;
 45         } else {
 46             this.owner = Thread.currentThread();
 47         }
 48     }
 49 
 50     /**
 51      * Creates a stackable scope owned by the current thread.
 52      */
 53     public StackableScope() {
 54         this(false);
 55     }
 56 
 57     /**
 58      * Returns the scope owner or null is not owned.
 59      */
 60     public Thread owner() {
 61         return owner;
 62     }
 63 
 64     /**
 65      * Pushes this scope onto the current thread's scope stack.
 66      */
 67     public StackableScope push() {
 68         if (owner == null)
 69             throw new UnsupportedOperationException();
 70         assert Thread.currentThread() == owner;
 71         previous = head();
 72         setHead(this);
 73         return this;
 74     }
 75 
 76     /**
 77      * Pops this scope from the current thread's scope stack if the scope is
 78      * at the top of stack.
 79      * @return true if the pop succeeded, false if this scope is not the top of stack
 80      */
 81     public boolean tryPop() {
 82         if (Thread.currentThread() != owner)
 83             throw new IllegalStateException("Not owner");
 84         if (head() == this) {
 85             setHead(previous);
 86             previous = null;
 87             return true;
 88         } else {
 89             return false;
 90         }
 91     }
 92 
 93     /**
 94      * Pops this scope from the current thread's scope stack.
 95      *
 96      * For well behaved usages, this scope is at the top of the stack. It is popped
 97      * from the stack and the method returns {@code true}.
 98      *
 99      * If this scope is not at the top of the stack then this method attempts to
100      * close each of the intermediate scopes by invoking their {@link #tryClose()}
101      * method. If tryClose succeeds then the scope is removed from the stack. When
102      * done, this scope is removed from the stack and {@code false} is returned.
103      *
104      * This method does nothing, and returns {@code false}, if this scope is not
105      * on the current thread's scope stack.
106      *
107      * @return true if this scope was at the top of the stack, otherwise false
108      */
109     public boolean popForcefully() {
110         if (Thread.currentThread() != owner)
111             throw new IllegalStateException("Not owner");
112         final StackableScope head = head();
113         if (head == this) {
114             setHead(previous);
115             previous = null;
116             return true;
117         }
118 
119         // scope is not the top of stack
120         if (contains(this)) {
121             StackableScope current = head;
122             while (current != this) {
123                 StackableScope previous = current.previous();
124                 // attempt to forcefully close the scope and remove from stack
125                 if (current.tryClose()) {
126                     current.unlink();
127                 }
128                 current = previous;
129             }
130             unlink();
131         }
132         return false;
133     }
134 
135     /**
136      * Pops all scopes from the current thread's scope stack.
137      */
138     public static void popAll() {
139         StackableScope head = head();
140         if (head != null) {
141             StackableScope current = head;
142             while (current != null) {
143                 assert Thread.currentThread() == current.owner();
144                 current.tryClose();
145                 current = current.previous();
146             }
147             setHead(null);
148         }
149     }
150 
151     /**
152      * Returns the scope that encloses this scope.
153      */
154     public StackableScope enclosingScope() {
155         StackableScope previous = this.previous;
156         if (previous != null)
157             return previous;
158         if (owner != null)
159             return JLA.threadContainer(owner);
160         return null;
161     }
162 
163     /**
164      * Returns the scope of the given type that encloses this scope.
165      */
166     public <T extends StackableScope> T enclosingScope(Class<T> type) {
167         StackableScope current = enclosingScope();
168         while (current != null) {
169             if (type.isInstance(current)) {
170                 @SuppressWarnings("unchecked")
171                 T tmp = (T) current;
172                 return tmp;
173             }
174             current = current.enclosingScope();
175         }
176         return null;
177     }
178 
179     /**
180      * Returns the scope that directly encloses this scope, null if none.
181      */
182     StackableScope previous() {
183         return previous;
184     }
185 
186     /**
187      * Returns the scope that this scope directly encloses, null if none.
188      */
189     private StackableScope next() {
190         assert contains(this);
191         StackableScope current = head();
192         StackableScope next = null;
193         while (current != this) {
194             next = current;
195             current = current.previous();
196         }
197         return next;
198     }
199 
200     /**
201      * Override this method to close this scope and release its resources.
202      * This method should not pop the scope from the stack.
203      * This method is guaranteed to execute on the owner thread.
204      * @return true if this method closed the scope, false if it failed
205      */
206     protected boolean tryClose() {
207         assert Thread.currentThread() == owner;
208         return false;
209     }
210 
211     /**
212      * Removes this scope from the current thread's scope stack.
213      */
214     private void unlink() {
215         assert contains(this);
216         StackableScope next = next();
217         if (next == null) {
218             setHead(previous);
219         } else {
220             next.previous = previous;
221         }
222         previous = null;
223     }
224 
225     /**
226      * Returns true if the given scope is on the current thread's scope stack.
227      */
228     private static boolean contains(StackableScope scope) {
229         assert scope != null;
230         StackableScope current = head();
231         while (current != null && current != scope) {
232             current = current.previous();
233         }
234         return (current == scope);
235     }
236 
237     /**
238      * Returns the head of the current thread's scope stack.
239      */
240     private static StackableScope head() {
241         return JLA.headStackableScope(Thread.currentThread());
242     }
243 
244     /**
245      * Sets the head (top) of the current thread's scope stack.
246      */
247     private static void setHead(StackableScope scope) {
248         JLA.setHeadStackableScope(scope);
249     }
250 
251 }