Skip to content

Commit fedc5fa

Browse files
committedSep 27, 2023
8317050: Scope should reflect lifetime of underying resource
Reviewed-by: jvernee
1 parent 319382b commit fedc5fa

12 files changed

+225
-37
lines changed
 

‎src/java.base/share/classes/java/lang/foreign/Arena.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ static Arena ofAuto() {
219219
*/
220220
static Arena global() {
221221
class Holder {
222-
static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena();
222+
static final Arena GLOBAL = MemorySessionImpl.createGlobal().asArena();
223223
}
224224
return Holder.GLOBAL;
225225
}

‎src/java.base/share/classes/java/lang/foreign/Linker.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@
365365
*
366366
* The size of the segment returned by the {@code malloc} downcall method handle is
367367
* <a href="MemorySegment.html#wrapping-addresses">zero</a>. Moreover, the scope of the
368-
* returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must,
368+
* returned segment is a scope that is always alive. To provide safe access to the segment, we must,
369369
* unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to
370370
* attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory
371371
* backing the segment can be managed automatically, as for any other native segment created directly from Java code.
@@ -599,7 +599,7 @@ static Linker nativeLinker() {
599599
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
600600
* <p>
601601
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
602-
* is a native segment associated with a fresh scope that is always alive.
602+
* is a native segment associated with a scope that is always alive.
603603
* Under normal conditions, the size of this segment argument is {@code 0}.
604604
* However, if the address layout has a {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the
605605
* segment argument is set to {@code T.byteSize()}.

‎src/java.base/share/classes/java/lang/foreign/MemoryLayout.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ default MethodHandle byteOffsetHandle(PathElement... elements) {
460460
* </ul>
461461
* <p>
462462
* If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)}
463-
* on the returned var handle will return a new memory segment. The segment is associated with a fresh scope that is
463+
* on the returned var handle will return a new memory segment. The segment is associated with a scope that is
464464
* always alive. Moreover, the size of the segment depends on whether the address layout has a
465465
* {@linkplain AddressLayout#targetLayout() target layout}. More specifically:
466466
* <ul>

‎src/java.base/share/classes/java/lang/foreign/MemorySegment.java

+25-5
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@
382382
* of memory whose size is not known, any access operations involving these segments cannot be validated.
383383
* In effect, a zero-length memory segment <em>wraps</em> an address, and it cannot be used without explicit intent
384384
* (see below);</li>
385-
* <li>The segment is associated with a fresh scope that is always alive. Thus, while zero-length
385+
* <li>The segment is associated with a scope that is always alive. Thus, while zero-length
386386
* memory segments cannot be accessed directly, they can be passed, opaquely, to other pointer-accepting foreign functions.</li>
387387
* </ul>
388388
* <p>
@@ -633,7 +633,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) {
633633
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
634634
* .reinterpret(byteSize());
635635
* }
636-
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
636+
* That is, the cleanup action receives a segment that is associated with a scope that is always alive,
637637
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}.
638638
* <p>
639639
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
@@ -671,7 +671,7 @@ default MemorySegment asSlice(long offset, MemoryLayout layout) {
671671
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
672672
* .reinterpret(newSize);
673673
* }
674-
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
674+
* That is, the cleanup action receives a segment that is associated with a scope that is always alive,
675675
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}.
676676
* <p>
677677
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
@@ -1714,7 +1714,7 @@ default void set(ValueLayout.OfDouble layout, long offset, double value) {
17141714

17151715
/**
17161716
* Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in
1717-
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
1717+
* a native segment, associated with a scope that is always alive. Under normal conditions,
17181718
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
17191719
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
17201720
* is set to {@code T.byteSize()}.
@@ -2153,7 +2153,7 @@ default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
21532153

21542154
/**
21552155
* Reads an address from this segment at the given at the given index, scaled by the given layout size. The read address is wrapped in
2156-
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
2156+
* a native segment, associated with a scope that is always alive. Under normal conditions,
21572157
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
21582158
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
21592159
* is set to {@code T.byteSize()}.
@@ -2366,6 +2366,26 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff
23662366
* <p>
23672367
* Scope instances can be compared for equality. That is, two scopes
23682368
* are considered {@linkplain #equals(Object)} if they denote the same lifetime.
2369+
* <p>
2370+
* If two memory segments are obtained from the same {@linkplain #ofBuffer(Buffer) buffer}
2371+
* or {@linkplain #ofArray(int[]) array}, the scopes associated with said segments are considered
2372+
* {@linkplain #equals(Object) equal}, as the two segments have the same lifetime:
2373+
* {@snippet lang=java :
2374+
* byte[] arr = new byte[10];
2375+
* MemorySegment segment1 = MemorySegment.ofArray(arr);
2376+
* MemorySegment segment2 = MemorySegment.ofArray(arr);
2377+
* assert segment1.scope().equals(segment2.scope());
2378+
* }
2379+
* <p>
2380+
* If two distinct memory segments are <a href="#wrapping-addresses">zero-length memory segments</a>, their scopes
2381+
* are always considered {@linkplain #equals(Object) equal}:
2382+
* {@snippet lang=java :
2383+
* MemorySegment segment1 = MemorySegment.ofAddress(42L);
2384+
* MemorySegment segment2 = MemorySegment.ofAddress(1000L);
2385+
* assert segment1.scope().equals(segment2.scope());
2386+
* }
2387+
* The scope of a zero-length memory segment can always be overridden using the
2388+
* {@link MemorySegment#reinterpret(Arena, Consumer)} method.
23692389
*/
23702390
sealed interface Scope permits MemorySessionImpl {
23712391
/**

‎src/java.base/share/classes/java/lang/foreign/SymbolLookup.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ default SymbolLookup or(SymbolLookup other) {
164164
* <p>
165165
* Libraries associated with a class loader are unloaded when the class loader becomes
166166
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
167-
* returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's
167+
* returned by this method is associated with a {@linkplain MemorySegment.Scope scope} which keeps the caller's
168168
* class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
169169
* (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
170170
* obtained by it, is reachable.
@@ -188,7 +188,7 @@ static SymbolLookup loaderLookup() {
188188
if ((loader == null || loader instanceof BuiltinClassLoader)) {
189189
loaderArena = Arena.global();
190190
} else {
191-
MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
191+
MemorySessionImpl session = MemorySessionImpl.createHeap(loader);
192192
loaderArena = session.asArena();
193193
}
194194
return name -> {

‎src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@
4848
import jdk.internal.access.SharedSecrets;
4949
import jdk.internal.access.foreign.UnmapperProxy;
5050
import jdk.internal.misc.ScopedMemoryAccess;
51-
import jdk.internal.misc.Unsafe;
5251
import jdk.internal.reflect.CallerSensitive;
5352
import jdk.internal.reflect.Reflection;
5453
import jdk.internal.util.ArraysSupport;
5554
import jdk.internal.util.Preconditions;
5655
import jdk.internal.vm.annotation.ForceInline;
56+
import sun.nio.ch.DirectBuffer;
5757

5858
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
5959

@@ -537,7 +537,7 @@ public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) {
537537
if (bufferSegment != null) {
538538
bufferScope = bufferSegment.scope;
539539
} else {
540-
bufferScope = MemorySessionImpl.heapSession(bb);
540+
bufferScope = MemorySessionImpl.createHeap(bufferRef(bb));
541541
}
542542
if (base != null) {
543543
return switch (base) {
@@ -565,6 +565,17 @@ public static AbstractMemorySegmentImpl ofBuffer(Buffer bb) {
565565
}
566566
}
567567

568+
private static Object bufferRef(Buffer buffer) {
569+
if (buffer instanceof DirectBuffer directBuffer) {
570+
// direct buffer, return either the buffer attachment (for slices and views), or the buffer itself
571+
return directBuffer.attachment() != null ?
572+
directBuffer.attachment() : directBuffer;
573+
} else {
574+
// heap buffer, return the underlying array
575+
return NIO_ACCESS.getBufferBase(buffer);
576+
}
577+
}
578+
568579
@ForceInline
569580
public static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long srcOffset,
570581
MemorySegment dstSegment, ValueLayout dstElementLayout, long dstOffset,

‎src/java.base/share/classes/jdk/internal/foreign/GlobalSession.java

+36-5
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,23 @@
2525

2626
package jdk.internal.foreign;
2727

28+
import jdk.internal.access.JavaNioAccess;
29+
import jdk.internal.access.SharedSecrets;
2830
import jdk.internal.vm.annotation.ForceInline;
31+
import sun.nio.ch.DirectBuffer;
32+
33+
import java.nio.Buffer;
34+
import java.util.Objects;
2935

3036
/**
3137
* The global, non-closeable, shared session. Similar to a shared session, but its {@link #close()} method throws unconditionally.
3238
* Adding new resources to the global session, does nothing: as the session can never become not-alive, there is nothing to track.
3339
* Acquiring and or releasing a memory session similarly does nothing.
3440
*/
35-
final class GlobalSession extends MemorySessionImpl {
36-
37-
final Object ref;
41+
non-sealed class GlobalSession extends MemorySessionImpl {
3842

39-
public GlobalSession(Object ref) {
43+
public GlobalSession() {
4044
super(null, null);
41-
this.ref = ref;
4245
}
4346

4447
@Override
@@ -67,4 +70,32 @@ void addInternal(ResourceList.ResourceCleanup resource) {
6770
public void justClose() {
6871
throw nonCloseable();
6972
}
73+
74+
/**
75+
* This is a global session that wraps a heap object. Possible objects are: Java arrays, buffers and
76+
* class loaders. Objects of two heap sessions are compared by identity. That is, if the wrapped object is the same,
77+
* then the resulting heap sessions are also considered equals. We do not compare the objects using
78+
* {@link Object#equals(Object)}, as that would be problematic when comparing buffers, whose equality and
79+
* hash codes are content-dependent.
80+
*/
81+
static class HeapSession extends GlobalSession {
82+
83+
final Object ref;
84+
85+
public HeapSession(Object ref) {
86+
super();
87+
this.ref = Objects.requireNonNull(ref);
88+
}
89+
90+
@Override
91+
public boolean equals(Object obj) {
92+
return obj instanceof HeapSession session &&
93+
ref == session.ref;
94+
}
95+
96+
@Override
97+
public int hashCode() {
98+
return System.identityHashCode(ref);
99+
}
100+
}
70101
}

‎src/java.base/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static MemorySegment fromArray(byte[] arr) {
115115
Objects.requireNonNull(arr);
116116
long byteSize = (long)arr.length * Utils.BaseAndScale.BYTE.scale();
117117
return new OfByte(Utils.BaseAndScale.BYTE.base(), arr, byteSize, false,
118-
MemorySessionImpl.heapSession(arr));
118+
MemorySessionImpl.createHeap(arr));
119119
}
120120

121121
@Override
@@ -149,7 +149,7 @@ public static MemorySegment fromArray(char[] arr) {
149149
Objects.requireNonNull(arr);
150150
long byteSize = (long)arr.length * Utils.BaseAndScale.CHAR.scale();
151151
return new OfChar(Utils.BaseAndScale.CHAR.base(), arr, byteSize, false,
152-
MemorySessionImpl.heapSession(arr));
152+
MemorySessionImpl.createHeap(arr));
153153
}
154154

155155
@Override
@@ -183,7 +183,7 @@ public static MemorySegment fromArray(short[] arr) {
183183
Objects.requireNonNull(arr);
184184
long byteSize = (long)arr.length * Utils.BaseAndScale.SHORT.scale();
185185
return new OfShort(Utils.BaseAndScale.SHORT.base(), arr, byteSize, false,
186-
MemorySessionImpl.heapSession(arr));
186+
MemorySessionImpl.createHeap(arr));
187187
}
188188

189189
@Override
@@ -217,7 +217,7 @@ public static MemorySegment fromArray(int[] arr) {
217217
Objects.requireNonNull(arr);
218218
long byteSize = (long)arr.length * Utils.BaseAndScale.INT.scale();
219219
return new OfInt(Utils.BaseAndScale.INT.base(), arr, byteSize, false,
220-
MemorySessionImpl.heapSession(arr));
220+
MemorySessionImpl.createHeap(arr));
221221
}
222222

223223
@Override
@@ -251,7 +251,7 @@ public static MemorySegment fromArray(long[] arr) {
251251
Objects.requireNonNull(arr);
252252
long byteSize = (long)arr.length * Utils.BaseAndScale.LONG.scale();
253253
return new OfLong(Utils.BaseAndScale.LONG.base(), arr, byteSize, false,
254-
MemorySessionImpl.heapSession(arr));
254+
MemorySessionImpl.createHeap(arr));
255255
}
256256

257257
@Override
@@ -285,7 +285,7 @@ public static MemorySegment fromArray(float[] arr) {
285285
Objects.requireNonNull(arr);
286286
long byteSize = (long)arr.length * Utils.BaseAndScale.FLOAT.scale();
287287
return new OfFloat(Utils.BaseAndScale.FLOAT.base(), arr, byteSize, false,
288-
MemorySessionImpl.heapSession(arr));
288+
MemorySessionImpl.createHeap(arr));
289289
}
290290

291291
@Override
@@ -319,7 +319,7 @@ public static MemorySegment fromArray(double[] arr) {
319319
Objects.requireNonNull(arr);
320320
long byteSize = (long)arr.length * Utils.BaseAndScale.DOUBLE.scale();
321321
return new OfDouble(Utils.BaseAndScale.DOUBLE.base(), arr, byteSize, false,
322-
MemorySessionImpl.heapSession(arr));
322+
MemorySessionImpl.createHeap(arr));
323323
}
324324

325325
@Override

‎src/java.base/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, bo
4848

4949
@Override
5050
ByteBuffer makeByteBuffer() {
51-
return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null,
52-
scope == MemorySessionImpl.GLOBAL ? null : this);
51+
return NIO_ACCESS.newMappedByteBuffer(unmapper, min, (int)length, null, this);
5352
}
5453

5554
@Override

‎src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java

+12-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import java.lang.invoke.VarHandle;
3434
import java.lang.ref.Cleaner;
3535
import java.util.Objects;
36+
37+
import jdk.internal.foreign.GlobalSession.HeapSession;
3638
import jdk.internal.misc.ScopedMemoryAccess;
3739
import jdk.internal.vm.annotation.ForceInline;
3840

@@ -59,10 +61,10 @@ public abstract sealed class MemorySessionImpl
5961
static final VarHandle STATE;
6062
static final int MAX_FORKS = Integer.MAX_VALUE;
6163

62-
public static final MemorySessionImpl GLOBAL = new GlobalSession(null);
63-
6464
static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed);
6565
static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread);
66+
// This is the session of all zero-length memory segments
67+
static final GlobalSession NATIVE_SESSION = new GlobalSession();
6668

6769
final ResourceList resourceList;
6870
final Thread owner;
@@ -143,6 +145,14 @@ public static MemorySessionImpl createImplicit(Cleaner cleaner) {
143145
return new ImplicitSession(cleaner);
144146
}
145147

148+
public static MemorySessionImpl createGlobal() {
149+
return new GlobalSession();
150+
}
151+
152+
public static MemorySessionImpl createHeap(Object ref) {
153+
return new HeapSession(ref);
154+
}
155+
146156
public abstract void release0();
147157

148158
public abstract void acquire0();
@@ -230,10 +240,6 @@ public void close() {
230240

231241
abstract void justClose();
232242

233-
public static MemorySessionImpl heapSession(Object ref) {
234-
return new GlobalSession(ref);
235-
}
236-
237243
/**
238244
* A list of all cleanup actions associated with a memory session. Cleanup actions are modelled as instances
239245
* of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a session

‎src/java.base/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
6565
*/
6666
@ForceInline
6767
public NativeMemorySegmentImpl() {
68-
super(0L, false, new GlobalSession(null));
68+
super(0L, false, MemorySessionImpl.NATIVE_SESSION);
6969
this.min = 0L;
7070
}
7171

@@ -87,8 +87,7 @@ NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySess
8787

8888
@Override
8989
ByteBuffer makeByteBuffer() {
90-
return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null,
91-
scope == MemorySessionImpl.GLOBAL ? null : this);
90+
return NIO_ACCESS.newDirectByteBuffer(min, (int) this.length, null, this);
9291
}
9392

9493
@Override
@@ -176,6 +175,6 @@ public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize,
176175

177176
@ForceInline
178177
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
179-
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
178+
return new NativeMemorySegmentImpl(min, byteSize, false, MemorySessionImpl.NATIVE_SESSION);
180179
}
181180
}

‎test/jdk/java/foreign/TestScope.java

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) 2023, 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @run testng/othervm --enable-native-access=ALL-UNNAMED TestScope
27+
*/
28+
29+
import org.testng.annotations.*;
30+
31+
import java.lang.foreign.Arena;
32+
import java.lang.foreign.MemorySegment;
33+
import java.lang.foreign.SymbolLookup;
34+
import java.nio.ByteBuffer;
35+
import java.nio.IntBuffer;
36+
37+
import static org.testng.Assert.*;
38+
39+
public class TestScope {
40+
41+
static {
42+
System.loadLibrary("LookupTest");
43+
}
44+
45+
@Test
46+
public void testDifferentArrayScope() {
47+
MemorySegment.Scope scope1 = MemorySegment.ofArray(new byte[10]).scope();
48+
MemorySegment.Scope scope2 = MemorySegment.ofArray(new byte[10]).scope();
49+
assertNotEquals(scope1, scope2);
50+
}
51+
52+
@Test
53+
public void testDifferentBufferScope() {
54+
MemorySegment.Scope scope1 = MemorySegment.ofBuffer(ByteBuffer.allocateDirect(10)).scope();
55+
MemorySegment.Scope scope2 = MemorySegment.ofBuffer(ByteBuffer.allocateDirect(10)).scope();
56+
assertNotEquals(scope1, scope2);
57+
}
58+
59+
@Test
60+
public void testDifferentArenaScope() {
61+
MemorySegment.Scope scope1 = Arena.ofAuto().allocate(10).scope();
62+
MemorySegment.Scope scope2 = Arena.ofAuto().allocate(10).scope();
63+
assertNotEquals(scope1, scope2);
64+
}
65+
66+
@Test
67+
public void testSameArrayScope() {
68+
byte[] arr = new byte[10];
69+
assertEquals(MemorySegment.ofArray(arr).scope(), MemorySegment.ofArray(arr).scope());
70+
ByteBuffer buf = ByteBuffer.wrap(arr);
71+
assertEquals(MemorySegment.ofArray(arr).scope(), MemorySegment.ofBuffer(buf).scope());
72+
testDerivedBufferScope(MemorySegment.ofArray(arr));
73+
}
74+
75+
@Test
76+
public void testSameBufferScope() {
77+
ByteBuffer buf = ByteBuffer.allocateDirect(10);
78+
assertEquals(MemorySegment.ofBuffer(buf).scope(), MemorySegment.ofBuffer(buf).scope());
79+
testDerivedBufferScope(MemorySegment.ofBuffer(buf));
80+
}
81+
82+
@Test
83+
public void testSameArenaScope() {
84+
try (Arena arena = Arena.ofConfined()) {
85+
MemorySegment segment1 = arena.allocate(10);
86+
MemorySegment segment2 = arena.allocate(10);
87+
assertEquals(segment1.scope(), segment2.scope());
88+
testDerivedBufferScope(segment1);
89+
}
90+
}
91+
92+
@Test
93+
public void testSameNativeScope() {
94+
MemorySegment segment1 = MemorySegment.ofAddress(42);
95+
MemorySegment segment2 = MemorySegment.ofAddress(43);
96+
assertEquals(segment1.scope(), segment2.scope());
97+
assertEquals(segment1.scope(), segment2.reinterpret(10).scope());
98+
assertNotEquals(segment1.scope(), Arena.global().scope());
99+
testDerivedBufferScope(segment1.reinterpret(10));
100+
}
101+
102+
@Test
103+
public void testSameLookupScope() {
104+
SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
105+
MemorySegment segment1 = loaderLookup.find("f").get();
106+
MemorySegment segment2 = loaderLookup.find("c").get();
107+
assertEquals(segment1.scope(), segment2.scope());
108+
testDerivedBufferScope(segment1.reinterpret(10));
109+
}
110+
111+
void testDerivedBufferScope(MemorySegment segment) {
112+
ByteBuffer buffer = segment.asByteBuffer();
113+
MemorySegment.Scope expectedScope = segment.scope();
114+
assertEquals(MemorySegment.ofBuffer(buffer).scope(), expectedScope);
115+
// buffer slices should have same scope
116+
ByteBuffer slice = buffer.slice(0, 2);
117+
assertEquals(expectedScope, MemorySegment.ofBuffer(slice).scope());
118+
// buffer views should have same scope
119+
IntBuffer view = buffer.asIntBuffer();
120+
assertEquals(expectedScope, MemorySegment.ofBuffer(view).scope());
121+
}
122+
}

0 commit comments

Comments
 (0)
Please sign in to comment.