8343394: Make MemorySessionImpl.state a stable field

Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org>
Reviewed-by: mcimadamore, jvernee
This commit is contained in:
Quan Anh Mai 2024-11-07 14:32:18 +00:00
parent d2b681d455
commit 1d117f65f0
7 changed files with 250 additions and 43 deletions

View file

@ -41,8 +41,7 @@ final class ConfinedSession extends MemorySessionImpl {
private int asyncReleaseCount = 0;
static final VarHandle ASYNC_RELEASE_COUNT= MhUtil.findVarHandle(
MethodHandles.lookup(), "asyncReleaseCount", int.class);
static final VarHandle ASYNC_RELEASE_COUNT= MhUtil.findVarHandle(MethodHandles.lookup(), "asyncReleaseCount", int.class);
public ConfinedSession(Thread owner) {
super(owner, new ConfinedResourceList());
@ -52,17 +51,17 @@ final class ConfinedSession extends MemorySessionImpl {
@ForceInline
public void acquire0() {
checkValidState();
if (state == MAX_FORKS) {
if (acquireCount == MAX_FORKS) {
throw tooManyAcquires();
}
state++;
acquireCount++;
}
@Override
@ForceInline
public void release0() {
if (Thread.currentThread() == owner) {
state--;
acquireCount--;
} else {
// It is possible to end up here in two cases: this session was kept alive by some other confined session
// which is implicitly released (in which case the release call comes from the cleaner thread). Or,
@ -75,11 +74,11 @@ final class ConfinedSession extends MemorySessionImpl {
void justClose() {
checkValidState();
int asyncCount = (int)ASYNC_RELEASE_COUNT.getVolatile(this);
if ((state == 0 && asyncCount == 0)
|| ((state - asyncCount) == 0)) {
int acquire = acquireCount - asyncCount;
if (acquire == 0) {
state = CLOSED;
} else {
throw alreadyAcquired(state - asyncCount);
throw alreadyAcquired(acquire);
}
}

View file

@ -42,6 +42,7 @@ non-sealed class GlobalSession extends MemorySessionImpl {
public GlobalSession() {
super(null, null);
this.state = NONCLOSEABLE;
}
@Override
@ -50,11 +51,6 @@ non-sealed class GlobalSession extends MemorySessionImpl {
// do nothing
}
@Override
public boolean isCloseable() {
return false;
}
@Override
@ForceInline
public void acquire0() {

View file

@ -42,6 +42,7 @@ final class ImplicitSession extends SharedSession {
public ImplicitSession(Cleaner cleaner) {
super();
this.state = NONCLOSEABLE;
cleaner.register(this, resourceList);
}
@ -55,11 +56,6 @@ final class ImplicitSession extends SharedSession {
// do nothing
}
@Override
public boolean isCloseable() {
return false;
}
@Override
public void justClose() {
throw nonCloseable();

View file

@ -38,6 +38,7 @@ import jdk.internal.foreign.GlobalSession.HeapSession;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.invoke.MhUtil;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
/**
* This class manages the temporal bounds associated with a memory segment as well
@ -55,11 +56,19 @@ import jdk.internal.vm.annotation.ForceInline;
public abstract sealed class MemorySessionImpl
implements Scope
permits ConfinedSession, GlobalSession, SharedSession {
/**
* The value of the {@code state} of a {@code MemorySessionImpl}. The only possible transition
* is OPEN -> CLOSED. As a result, the states CLOSED and NONCLOSEABLE are stable. This allows
* us to annotate {@code state} with {@link Stable} and elide liveness check on non-closeable
* constant scopes, such as {@code GLOBAL_SESSION}.
*/
static final int OPEN = 0;
static final int CLOSED = -1;
static final int NONCLOSEABLE = 1;
static final VarHandle STATE = MhUtil.findVarHandle(
MethodHandles.lookup(), "state", int.class);
static final VarHandle STATE = MhUtil.findVarHandle(MethodHandles.lookup(), "state", int.class);
static final VarHandle ACQUIRE_COUNT = MhUtil.findVarHandle(MethodHandles.lookup(), "acquireCount", int.class);
static final int MAX_FORKS = Integer.MAX_VALUE;
@ -70,7 +79,11 @@ public abstract sealed class MemorySessionImpl
final ResourceList resourceList;
final Thread owner;
int state = OPEN;
@Stable
int state;
int acquireCount;
public Arena asArena() {
return new ArenaImpl(this);
@ -214,8 +227,8 @@ public abstract sealed class MemorySessionImpl
throw new CloneNotSupportedException();
}
public boolean isCloseable() {
return true;
public final boolean isCloseable() {
return state <= OPEN;
}
/**

View file

@ -44,6 +44,8 @@ sealed class SharedSession extends MemorySessionImpl permits ImplicitSession {
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
private static final int CLOSED_ACQUIRE_COUNT = -1;
SharedSession() {
super(null, new SharedResourceList());
}
@ -53,15 +55,15 @@ sealed class SharedSession extends MemorySessionImpl permits ImplicitSession {
public void acquire0() {
int value;
do {
value = (int) STATE.getVolatile(this);
if (value < OPEN) {
value = (int) ACQUIRE_COUNT.getVolatile(this);
if (value < 0) {
//segment is not open!
throw alreadyClosed();
throw sharedSessionAlreadyClosed();
} else if (value == MAX_FORKS) {
//overflow
throw tooManyAcquires();
}
} while (!STATE.compareAndSet(this, value, value + 1));
} while (!ACQUIRE_COUNT.compareAndSet(this, value, value + 1));
}
@Override
@ -69,24 +71,35 @@ sealed class SharedSession extends MemorySessionImpl permits ImplicitSession {
public void release0() {
int value;
do {
value = (int) STATE.getVolatile(this);
if (value <= OPEN) {
value = (int) ACQUIRE_COUNT.getVolatile(this);
if (value <= 0) {
//cannot get here - we can't close segment twice
throw alreadyClosed();
throw sharedSessionAlreadyClosed();
}
} while (!STATE.compareAndSet(this, value, value - 1));
} while (!ACQUIRE_COUNT.compareAndSet(this, value, value - 1));
}
void justClose() {
int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSED);
if (prevState < 0) {
throw alreadyClosed();
} else if (prevState != OPEN) {
throw alreadyAcquired(prevState);
int acquireCount = (int) ACQUIRE_COUNT.compareAndExchange(this, 0, CLOSED_ACQUIRE_COUNT);
if (acquireCount < 0) {
throw sharedSessionAlreadyClosed();
} else if (acquireCount > 0) {
throw alreadyAcquired(acquireCount);
}
STATE.setVolatile(this, CLOSED);
SCOPED_MEMORY_ACCESS.closeScope(this, ALREADY_CLOSED);
}
private IllegalStateException sharedSessionAlreadyClosed() {
// To avoid the situation where a scope fails to be acquired or closed but still reports as
// alive afterward, we wait for the state to change before throwing the exception
while ((int) STATE.getVolatile(this) == OPEN) {
Thread.onSpinWait();
}
return alreadyClosed();
}
/**
* A shared resource list; this implementation has to handle add vs. add races, as well as add vs. cleanup races.
*/