blob: 85a5646db253f5c4f28f0a3bd4b0bf1dc8164904 [file]
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.vm;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* A stackable scope to support structured constructs. The push method is used to
* push a StackableScope to the current thread's scope stack. The tryPop and
* popForcefully methods are used to pop the StackableScope from the current thread's
* scope stack.
*/
public class StackableScope {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
private final Thread owner;
private volatile StackableScope previous;
/**
* Creates a stackable scope.
* @param shared true for a shared scope that cannot be pushed to the stack,
* false for scope that is owned by the current thread
*/
StackableScope(boolean shared) {
if (shared) {
this.owner = null;
} else {
this.owner = Thread.currentThread();
}
}
/**
* Creates a stackable scope owned by the current thread.
*/
protected StackableScope() {
this(false);
}
/**
* Returns the scope owner or null if not owned.
*/
public Thread owner() {
return owner;
}
/**
* Pushes this scope onto the current thread's scope stack.
* @throws WrongThreadException it the current thread is not the owner
*/
public StackableScope push() {
if (Thread.currentThread() != owner)
throw new WrongThreadException("Not owner");
previous = head();
setHead(this);
return this;
}
/**
* Pops this scope from the current thread's scope stack if the scope is
* at the top of stack.
* @return true if the pop succeeded, false if this scope is not the top of stack
* @throws WrongThreadException it the current thread is not the owner
*/
@DontInline @ReservedStackAccess
public boolean tryPop() {
if (Thread.currentThread() != owner)
throw new WrongThreadException("Not owner");
if (head() == this) {
setHead(previous);
previous = null;
return true;
} else {
return false;
}
}
/**
* Pops this scope from the current thread's scope stack.
*
* For well-behaved usages, this scope is at the top of the stack. It is popped
* from the stack and the method returns {@code true}.
*
* If this scope is not at the top of the stack then this method attempts to
* close each of the intermediate scopes by invoking their {@link #tryClose()}
* method. If tryClose succeeds then the scope is removed from the stack. When
* done, this scope is removed from the stack and {@code false} is returned.
*
* This method does nothing, and returns {@code false}, if this scope is not
* on the current thread's scope stack.
*
* @return true if this scope was at the top of the stack, otherwise false
* @throws WrongThreadException it the current thread is not the owner
*/
@DontInline @ReservedStackAccess
public boolean popForcefully() {
if (Thread.currentThread() != owner)
throw new WrongThreadException("Not owner");
final StackableScope head = head();
if (head == this) {
setHead(previous);
previous = null;
return true;
}
// scope is not the top of stack
if (contains(this)) {
StackableScope current = head;
while (current != this) {
StackableScope previous = current.previous();
// attempt to forcefully close the scope and remove from stack
if (current.tryClose()) {
current.unlink();
}
current = previous;
}
unlink();
}
return false;
}
/**
* Pops all scopes from the current thread's scope stack.
*/
public static void popAll() {
StackableScope head = head();
if (head != null) {
StackableScope current = head;
while (current != null) {
assert Thread.currentThread() == current.owner();
current.tryClose();
current = current.previous();
}
setHead(null);
}
}
/**
* Returns the scope that encloses this scope.
*/
public StackableScope enclosingScope() {
StackableScope previous = this.previous;
if (previous != null)
return previous;
if (owner != null)
return JLA.threadContainer(owner);
return null;
}
/**
* Returns the scope of the given type that encloses this scope.
*/
public <T extends StackableScope> T enclosingScope(Class<T> type) {
StackableScope current = enclosingScope();
while (current != null) {
if (type.isInstance(current)) {
@SuppressWarnings("unchecked")
T tmp = (T) current;
return tmp;
}
current = current.enclosingScope();
}
return null;
}
/**
* Returns the scope that directly encloses this scope, null if none.
*/
StackableScope previous() {
return previous;
}
/**
* Returns the scope that this scope directly encloses, null if none.
*/
private StackableScope next() {
assert contains(this);
StackableScope current = head();
StackableScope next = null;
while (current != this) {
next = current;
current = current.previous();
}
return next;
}
/**
* Override this method to close this scope and release its resources.
* This method should not pop the scope from the stack.
* This method is guaranteed to execute on the owner thread.
* @return true if this method closed the scope, false if it failed
*/
protected boolean tryClose() {
assert Thread.currentThread() == owner;
return false;
}
/**
* Removes this scope from the current thread's scope stack.
*/
private void unlink() {
assert contains(this);
StackableScope next = next();
if (next == null) {
setHead(previous);
} else {
next.previous = previous;
}
previous = null;
}
/**
* Returns true if the given scope is on the current thread's scope stack.
*/
private static boolean contains(StackableScope scope) {
assert scope != null;
StackableScope current = head();
while (current != null && current != scope) {
current = current.previous();
}
return (current == scope);
}
/**
* Returns the head of the current thread's scope stack.
*/
static StackableScope head() {
return JLA.headStackableScope(Thread.currentThread());
}
/**
* Sets the head (top) of the current thread's scope stack.
*/
private static void setHead(StackableScope scope) {
JLA.setHeadStackableScope(scope);
}
}