Skip to content

Commit 454c59f

Browse files
mcimadamorepull[bot]
authored andcommitted
8279527: Dereferencing segments backed by different scopes leads to pollution
Reviewed-by: psandoz, jvernee
1 parent 7c5f0fb commit 454c59f

File tree

8 files changed

+64
-76
lines changed

8 files changed

+64
-76
lines changed

src/java.base/share/classes/java/nio/Buffer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,11 @@ final ScopedMemoryAccess.Scope scope() {
767767
final void checkScope() {
768768
ScopedMemoryAccess.Scope scope = scope();
769769
if (scope != null) {
770-
scope.checkValidState();
770+
try {
771+
scope.checkValidState();
772+
} catch (ScopedMemoryAccess.Scope.ScopedAccessError e) {
773+
throw new IllegalStateException("This segment is already closed");
774+
}
771775
}
772776
}
773777

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,7 @@ public void checkAccess(long offset, long length, boolean readOnly) {
364364
}
365365

366366
void checkValidState() {
367-
try {
368-
scope.checkValidState();
369-
} catch (ScopedMemoryAccess.Scope.ScopedAccessError ex) {
370-
throw new IllegalStateException("This segment is already closed");
371-
}
367+
scope.checkValidStateSlow();
372368
}
373369

374370
@Override

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ConfinedScope.java

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@
3939
*/
4040
final class ConfinedScope extends ResourceScopeImpl {
4141

42-
private boolean closed; // = false
43-
private int lockCount = 0;
4442
private int asyncReleaseCount = 0;
45-
private final Thread owner;
4643

4744
static final VarHandle ASYNC_RELEASE_COUNT;
4845

@@ -55,40 +52,29 @@ final class ConfinedScope extends ResourceScopeImpl {
5552
}
5653

5754
public ConfinedScope(Thread owner, Cleaner cleaner) {
58-
super(new ConfinedResourceList(), cleaner);
59-
this.owner = owner;
60-
}
61-
62-
@ForceInline
63-
public final void checkValidState() {
64-
if (owner != Thread.currentThread()) {
65-
throw new IllegalStateException("Attempted access outside owning thread");
66-
}
67-
if (closed) {
68-
throw new IllegalStateException("Already closed");
69-
}
55+
super(owner, new ConfinedResourceList(), cleaner);
7056
}
7157

7258
@Override
7359
public boolean isAlive() {
74-
return !closed;
60+
return state != CLOSED;
7561
}
7662

7763
@Override
7864
@ForceInline
7965
public void acquire0() {
80-
checkValidState();
81-
if (lockCount == MAX_FORKS) {
66+
checkValidStateSlow();
67+
if (state == MAX_FORKS) {
8268
throw new IllegalStateException("Scope keep alive limit exceeded");
8369
}
84-
lockCount++;
70+
state++;
8571
}
8672

8773
@Override
8874
@ForceInline
8975
public void release0() {
9076
if (Thread.currentThread() == owner) {
91-
lockCount--;
77+
state--;
9278
} else {
9379
// It is possible to end up here in two cases: this scope was kept alive by some other confined scope
9480
// which is implicitly released (in which case the release call comes from the cleaner thread). Or,
@@ -99,19 +85,14 @@ public void release0() {
9985
}
10086

10187
void justClose() {
102-
this.checkValidState();
103-
if (lockCount == 0 || lockCount - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
104-
closed = true;
88+
checkValidStateSlow();
89+
if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
90+
state = CLOSED;
10591
} else {
106-
throw new IllegalStateException("Scope is kept alive by " + lockCount + " scopes");
92+
throw new IllegalStateException("Scope is kept alive by " + state + " scopes");
10793
}
10894
}
10995

110-
@Override
111-
public Thread ownerThread() {
112-
return owner;
113-
}
114-
11596
/**
11697
* A confined resource list; no races are possible here.
11798
*/

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeSymbolImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@
2828
import jdk.incubator.foreign.MemoryAddress;
2929
import jdk.incubator.foreign.NativeSymbol;
3030
import jdk.incubator.foreign.ResourceScope;
31+
import jdk.internal.misc.ScopedMemoryAccess;
3132

3233
public record NativeSymbolImpl(String name, MemoryAddress address, ResourceScope scope) implements NativeSymbol, Scoped {
3334
@Override
3435
public MemoryAddress address() {
35-
((ResourceScopeImpl)scope).checkValidState();
36+
((ResourceScopeImpl)scope).checkValidStateSlow();
3637
return address;
3738
}
3839
}

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import jdk.internal.misc.ScopedMemoryAccess;
3333
import jdk.internal.vm.annotation.ForceInline;
3434

35+
import java.lang.invoke.MethodHandles;
36+
import java.lang.invoke.VarHandle;
3537
import java.lang.ref.Cleaner;
3638
import java.lang.ref.Reference;
3739
import java.util.Objects;
@@ -53,6 +55,23 @@ public abstract non-sealed class ResourceScopeImpl implements ResourceScope, Seg
5355

5456
final ResourceList resourceList;
5557
final Cleaner.Cleanable cleanable;
58+
final Thread owner;
59+
60+
static final int ALIVE = 0;
61+
static final int CLOSING = -1;
62+
static final int CLOSED = -2;
63+
64+
int state = ALIVE;
65+
66+
static final VarHandle STATE;
67+
68+
static {
69+
try {
70+
STATE = MethodHandles.lookup().findVarHandle(ResourceScopeImpl.class, "state", int.class);
71+
} catch (Throwable ex) {
72+
throw new ExceptionInInitializerError(ex);
73+
}
74+
}
5675

5776
static final int MAX_FORKS = Integer.MAX_VALUE;
5877

@@ -89,7 +108,8 @@ void addInternal(ResourceList.ResourceCleanup resource) {
89108
}
90109
}
91110

92-
protected ResourceScopeImpl(ResourceList resourceList, Cleaner cleaner) {
111+
protected ResourceScopeImpl(Thread owner, ResourceList resourceList, Cleaner cleaner) {
112+
this.owner = owner;
93113
this.resourceList = resourceList;
94114
cleanable = (cleaner != null) ?
95115
cleaner.register(this, resourceList) : null;
@@ -147,30 +167,41 @@ public void close() {
147167
* Returns "owner" thread of this scope.
148168
* @return owner thread (or null for a shared scope)
149169
*/
150-
public abstract Thread ownerThread();
170+
public final Thread ownerThread() {
171+
return owner;
172+
}
151173

152174
/**
153175
* Returns true, if this scope is still alive. This method may be called in any thread.
154176
* @return {@code true} if this scope is not closed yet.
155177
*/
156178
public abstract boolean isAlive();
157179

158-
159180
/**
160181
* This is a faster version of {@link #checkValidStateSlow()}, which is called upon memory access, and which
161-
* relies on invariants associated with the memory scope implementations (typically, volatile access
162-
* to the closed state bit is replaced with plain access, and ownership check is removed where not needed.
163-
* Should be used with care.
182+
* relies on invariants associated with the memory scope implementations (volatile access
183+
* to the closed state bit is replaced with plain access). This method should be monomorphic,
184+
* to avoid virtual calls in the memory access hot path. This method is not intended as general purpose method
185+
* and should only be used in the memory access handle hot path; for liveness checks triggered by other API methods,
186+
* please use {@link #checkValidStateSlow()}.
164187
*/
165-
public abstract void checkValidState();
188+
@ForceInline
189+
public final void checkValidState() {
190+
if (owner != null && owner != Thread.currentThread()) {
191+
throw new IllegalStateException("Attempted access outside owning thread");
192+
}
193+
if (state < ALIVE) {
194+
throw ScopedAccessError.INSTANCE;
195+
}
196+
}
166197

167198
/**
168199
* Checks that this scope is still alive (see {@link #isAlive()}).
169200
* @throws IllegalStateException if this scope is already closed or if this is
170201
* a confined scope and this method is called outside of the owner thread.
171202
*/
172203
public final void checkValidStateSlow() {
173-
if (ownerThread() != null && Thread.currentThread() != ownerThread()) {
204+
if (owner != null && Thread.currentThread() != owner) {
174205
throw new IllegalStateException("Attempted access outside owning thread");
175206
} else if (!isAlive()) {
176207
throw new IllegalStateException("Already closed");

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/SharedScope.java

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,36 +45,8 @@ class SharedScope extends ResourceScopeImpl {
4545

4646
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
4747

48-
private static final int ALIVE = 0;
49-
private static final int CLOSING = -1;
50-
private static final int CLOSED = -2;
51-
52-
private int state = ALIVE;
53-
54-
private static final VarHandle STATE;
55-
56-
static {
57-
try {
58-
STATE = MethodHandles.lookup().findVarHandle(jdk.internal.foreign.SharedScope.class, "state", int.class);
59-
} catch (Throwable ex) {
60-
throw new ExceptionInInitializerError(ex);
61-
}
62-
}
63-
6448
SharedScope(Cleaner cleaner) {
65-
super(new SharedResourceList(), cleaner);
66-
}
67-
68-
@Override
69-
public Thread ownerThread() {
70-
return null;
71-
}
72-
73-
@Override
74-
public void checkValidState() {
75-
if (state < ALIVE) {
76-
throw ScopedAccessError.INSTANCE;
77-
}
49+
super(null, new SharedResourceList(), cleaner);
7850
}
7951

8052
@Override

test/jdk/java/foreign/TestByteBuffer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInje
369369
Throwable cause = ex.getCause();
370370
if (cause instanceof IllegalStateException) {
371371
//all get/set buffer operation should fail because of the scope check
372-
assertTrue(ex.getCause().getMessage().contains("Already closed"));
372+
assertTrue(ex.getCause().getMessage().contains("already closed"));
373373
} else {
374374
//all other exceptions were unexpected - fail
375375
fail("Unexpected exception", cause);
@@ -406,7 +406,7 @@ public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
406406
handle.invoke(e.getValue());
407407
fail();
408408
} catch (IllegalStateException ex) {
409-
assertTrue(ex.getMessage().contains("Already closed"));
409+
assertTrue(ex.getMessage().contains("already closed"));
410410
} catch (UnsupportedOperationException ex) {
411411
//skip
412412
} catch (Throwable ex) {

test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverPollutedSegments.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class LoopOverPollutedSegments {
5858

5959
static final Unsafe unsafe = Utils.unsafe;
6060

61-
MemorySegment nativeSegment, heapSegmentBytes, heapSegmentFloats;
61+
MemorySegment nativeSegment, nativeSharedSegment, heapSegmentBytes, heapSegmentFloats;
6262
byte[] arr;
6363
long addr;
6464

@@ -73,6 +73,7 @@ public void setup() {
7373
}
7474
arr = new byte[ALLOC_SIZE];
7575
nativeSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, ResourceScope.newConfinedScope());
76+
nativeSharedSegment = MemorySegment.allocateNative(ALLOC_SIZE, 4, ResourceScope.newSharedScope());
7677
heapSegmentBytes = MemorySegment.ofArray(new byte[ALLOC_SIZE]);
7778
heapSegmentFloats = MemorySegment.ofArray(new float[ELEM_SIZE]);
7879

@@ -81,6 +82,8 @@ public void setup() {
8182
unsafe.putInt(arr, Unsafe.ARRAY_BYTE_BASE_OFFSET + (i * 4), i);
8283
nativeSegment.setAtIndex(JAVA_INT, i, i);
8384
nativeSegment.setAtIndex(JAVA_FLOAT, i, i);
85+
nativeSharedSegment.setAtIndex(JAVA_INT, i, i);
86+
nativeSharedSegment.setAtIndex(JAVA_FLOAT, i, i);
8487
intHandle.set(nativeSegment, (long)i, i);
8588
heapSegmentBytes.setAtIndex(JAVA_INT, i, i);
8689
heapSegmentBytes.setAtIndex(JAVA_FLOAT, i, i);

0 commit comments

Comments
 (0)