Skip to content

Commit b07da3a

Browse files
committed
8317819: Scope should reflect lifetime of underying resource (mainline)
Reviewed-by: jvernee
1 parent 6f1d896 commit b07da3a

File tree

12 files changed

+254
-64
lines changed

12 files changed

+254
-64
lines changed

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,19 @@
4343
* to obtain native segments.
4444
* <p>
4545
* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
46-
* features an <em>unbounded lifetime</em>. As such, native segments allocated with the global arena are always
47-
* accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the
48-
* global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
46+
* features an <em>unbounded lifetime</em>. The scope of the global arena is the global scope.
47+
* As such, native segments allocated with the global arena are always accessible and their backing regions
48+
* of memory are never deallocated.
49+
* Moreover, memory segments allocated with the global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
4950
* {@snippet lang = java:
5051
* MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()'
5152
* ...
5253
* // segment is never deallocated!
5354
*}
5455
* <p>
5556
* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena
56-
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. As such, the regions
57+
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. The scope
58+
* of an automatic arena is an automatic scope. As such, the regions
5759
* of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time
5860
* <em>after</em> the automatic arena (and all the segments allocated by it) becomes
5961
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
@@ -67,11 +69,9 @@
6769
* Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over
6870
* the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this,
6971
* namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature
70-
* bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined
71-
* arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory
72-
* backing memory segments allocated with a confined arena are deallocated when the confined arena is closed.
73-
* When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access
74-
* operations on these segments will fail {@link IllegalStateException}:
72+
* bounded lifetimes that are managed manually. For instance, when a confined arena is {@linkplain #close() closed}
73+
* successfully, its scope is {@linkplain Scope#isAlive() invalidated}. As a result, all the memory segments allocated
74+
* by the arena can no longer be accessed, and their regions of memory are deallocated:
7575
*
7676
* {@snippet lang = java:
7777
* MemorySegment segment = null;
@@ -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.GLOBAL_SESSION.asArena();
223223
}
224224
return Holder.GLOBAL;
225225
}

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

Lines changed: 3 additions & 3 deletions
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 the global scope. 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.
@@ -563,7 +563,7 @@ static Linker nativeLinker() {
563563
* <p>
564564
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout},
565565
* invoking the returned method handle will return a native segment associated with
566-
* a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}.
566+
* the global scope. Under normal conditions, the size of the returned segment is {@code 0}.
567567
* However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout() target layout}
568568
* {@code T}, then the size of the returned segment is set to {@code T.byteSize()}.
569569
* <p>
@@ -602,7 +602,7 @@ static Linker nativeLinker() {
602602
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
603603
* <p>
604604
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
605-
* is a native segment associated with a fresh scope that is always alive.
605+
* is a native segment associated with the global scope.
606606
* Under normal conditions, the size of this segment argument is {@code 0}.
607607
* However, if the address layout has a {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the
608608
* segment argument is set to {@code T.byteSize()}.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
492492
* </ul>
493493
* <p>
494494
* If the selected layout is an {@linkplain AddressLayout address layout}, calling {@link VarHandle#get(Object...)}
495-
* on the returned var handle will return a new memory segment. The segment is associated with a fresh scope that is
496-
* always alive. Moreover, the size of the segment depends on whether the address layout has a
495+
* on the returned var handle will return a new memory segment. The segment is associated with the global scope.
496+
* Moreover, the size of the segment depends on whether the address layout has a
497497
* {@linkplain AddressLayout#targetLayout() target layout}. More specifically:
498498
* <ul>
499499
* <li>If the address layout has a target layout {@code T}, then the size of the returned segment
@@ -513,7 +513,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
513513
* {@snippet lang = "java":
514514
* VarHandle baseHandle = this.varHandle(P);
515515
* MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get();
516-
* VarHandle targetHandle = target.varHandle(P');
516+
* VarHandle targetHandle = target.varHandle(P);
517517
* targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0
518518
* targetHandle = MethodHandles.collectCoordinates(targetHandle, 0,
519519
* baseHandle.toMethodHandle(VarHandle.AccessMode.GET));

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

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@
376376
* of memory whose size is not known, any access operations involving these segments cannot be validated.
377377
* In effect, a zero-length memory segment <em>wraps</em> an address, and it cannot be used without explicit intent
378378
* (see below);</li>
379-
* <li>The segment is associated with a fresh scope that is always alive. Thus, while zero-length
379+
* <li>The segment is associated with the global scope. Thus, while zero-length
380380
* memory segments cannot be accessed directly, they can be passed, opaquely, to other pointer-accepting foreign functions.</li>
381381
* </ul>
382382
* <p>
@@ -625,7 +625,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
625625
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
626626
* .reinterpret(byteSize());
627627
* }
628-
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
628+
* That is, the cleanup action receives a segment that is associated with the global scope,
629629
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@link #byteSize()}.
630630
* <p>
631631
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
@@ -664,7 +664,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
664664
* MemorySegment cleanupSegment = MemorySegment.ofAddress(this.address())
665665
* .reinterpret(newSize);
666666
* }
667-
* That is, the cleanup action receives a segment that is associated with a fresh scope that is always alive,
667+
* That is, the cleanup action receives a segment that is associated with the global scope,
668668
* and is accessible from any thread. The size of the segment accepted by the cleanup action is {@code newSize}.
669669
* <p>
670670
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
@@ -1155,11 +1155,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
11551155
* <p>
11561156
* If the provided buffer has been obtained by calling {@link #asByteBuffer()} on a memory segment whose
11571157
* {@linkplain Scope scope} is {@code S}, the returned segment will be associated with the
1158-
* same scope {@code S}. Otherwise, the scope of the returned segment is a fresh scope that is always alive.
1159-
* <p>
1160-
* The scope associated with the returned segment keeps the provided buffer reachable. As such, if
1161-
* the provided buffer is a direct buffer, its backing memory region will not be deallocated as long as the
1162-
* returned segment (or any of its slices) are kept reachable.
1158+
* same scope {@code S}. Otherwise, the scope of the returned segment is an automatic scope that keeps the provided
1159+
* buffer reachable. As such, if the provided buffer is a direct buffer, its backing memory region will not be
1160+
* deallocated as long as the returned segment (or any of its slices) are kept reachable.
11631161
*
11641162
* @param buffer the buffer instance to be turned into a new memory segment.
11651163
* @return a memory segment, derived from the given buffer instance.
@@ -1174,7 +1172,7 @@ static MemorySegment ofBuffer(Buffer buffer) {
11741172

11751173
/**
11761174
* Creates a heap segment backed by the on-heap region of memory that holds the given byte array.
1177-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1175+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
11781176
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
11791177
*
11801178
* @param byteArray the primitive array backing the heap memory segment.
@@ -1186,7 +1184,7 @@ static MemorySegment ofArray(byte[] byteArray) {
11861184

11871185
/**
11881186
* Creates a heap segment backed by the on-heap region of memory that holds the given char array.
1189-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1187+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
11901188
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
11911189
*
11921190
* @param charArray the primitive array backing the heap segment.
@@ -1198,7 +1196,7 @@ static MemorySegment ofArray(char[] charArray) {
11981196

11991197
/**
12001198
* Creates a heap segment backed by the on-heap region of memory that holds the given short array.
1201-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1199+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
12021200
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
12031201
*
12041202
* @param shortArray the primitive array backing the heap segment.
@@ -1210,7 +1208,7 @@ static MemorySegment ofArray(short[] shortArray) {
12101208

12111209
/**
12121210
* Creates a heap segment backed by the on-heap region of memory that holds the given int array.
1213-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1211+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
12141212
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
12151213
*
12161214
* @param intArray the primitive array backing the heap segment.
@@ -1222,7 +1220,7 @@ static MemorySegment ofArray(int[] intArray) {
12221220

12231221
/**
12241222
* Creates a heap segment backed by the on-heap region of memory that holds the given float array.
1225-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1223+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
12261224
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
12271225
*
12281226
* @param floatArray the primitive array backing the heap segment.
@@ -1234,7 +1232,7 @@ static MemorySegment ofArray(float[] floatArray) {
12341232

12351233
/**
12361234
* Creates a heap segment backed by the on-heap region of memory that holds the given long array.
1237-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1235+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
12381236
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
12391237
*
12401238
* @param longArray the primitive array backing the heap segment.
@@ -1246,7 +1244,7 @@ static MemorySegment ofArray(long[] longArray) {
12461244

12471245
/**
12481246
* Creates a heap segment backed by the on-heap region of memory that holds the given double array.
1249-
* The scope of the returned segment is a fresh scope that is always alive, and keeps the given array reachable.
1247+
* The scope of the returned segment is an automatic scope that keeps the given array reachable.
12501248
* The returned segment is always accessible, from any thread. Its {@link #address()} is set to zero.
12511249
*
12521250
* @param doubleArray the primitive array backing the heap segment.
@@ -1263,7 +1261,7 @@ static MemorySegment ofArray(double[] doubleArray) {
12631261

12641262
/**
12651263
* Creates a zero-length native segment from the given {@linkplain #address() address value}.
1266-
* The returned segment is associated with a scope that is always alive, and is accessible from any thread.
1264+
* The returned segment is associated with the global scope, and is accessible from any thread.
12671265
* <p>
12681266
* On 32-bit platforms, the given address value will be normalized such that the
12691267
* highest-order ("leftmost") 32 bits of the {@link MemorySegment#address() address}
@@ -1642,7 +1640,7 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr
16421640

16431641
/**
16441642
* Reads an address from this segment at the given offset, with the given layout. The read address is wrapped in
1645-
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
1643+
* a native segment, associated with the global scope. Under normal conditions,
16461644
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
16471645
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
16481646
* is set to {@code T.byteSize()}.
@@ -1994,7 +1992,7 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr
19941992

19951993
/**
19961994
* 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
1997-
* a native segment, associated with a fresh scope that is always alive. Under normal conditions,
1995+
* a native segment, associated with the global scope. Under normal conditions,
19981996
* the size of the returned segment is {@code 0}. However, if the provided address layout has a
19991997
* {@linkplain AddressLayout#targetLayout() target layout} {@code T}, then the size of the returned segment
20001998
* is set to {@code T.byteSize()}.
@@ -2192,11 +2190,38 @@ static long mismatch(MemorySegment srcSegment, long srcFromOffset, long srcToOff
21922190

21932191
/**
21942192
* A scope models the <em>lifetime</em> of all the memory segments associated with it. That is, a memory segment
2195-
* cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. A new scope is typically
2196-
* obtained indirectly, by creating a new {@linkplain Arena arena}.
2193+
* cannot be accessed if its associated scope is not {@linkplain #isAlive() alive}. Scope instances can be compared
2194+
* for equality. That is, two scopes are considered {@linkplain #equals(Object) equal} if they denote the same lifetime.
2195+
* <p>
2196+
* The lifetime of a memory segment can be either <em>unbounded</em> or <em>bounded</em>. An unbounded lifetime
2197+
* is modelled with the <em>global scope</em>. The global scope is always {@link #isAlive() alive}. As such, a segment
2198+
* associated with the global scope features trivial temporal bounds, and is always accessible.
2199+
* Segments associated with the global scope are:
2200+
* <ul>
2201+
* <li>Segments obtained from the {@linkplain Arena#global() global arena};</li>
2202+
* <li>Segments obtained from a raw address, using the {@link MemorySegment#ofAddress(long)} factory; and</li>
2203+
* <li><a href="#wrapping-addresses">Zero-length memory segments.</a></li>
2204+
* </ul>
21972205
* <p>
2198-
* Scope instances can be compared for equality. That is, two scopes
2199-
* are considered {@linkplain #equals(Object)} if they denote the same lifetime.
2206+
* Conversely, a bounded lifetime is modelled with a segment scope that can be invalidated, either {@link Arena#close() explicitly},
2207+
* or automatically, by the garbage collector. A segment scope that is invalidated automatically is an <em>automatic scope</em>.
2208+
* An automatic scope is always {@link #isAlive() alive} as long as it is <a href="../../../java/lang/ref/package.html#reachability">reachable</a>.
2209+
* Segments associated with an automatic scope are:
2210+
* <ul>
2211+
* <li>Segments obtained from an {@linkplain Arena#ofAuto() automatic arena};</li>
2212+
* <li>Segments obtained from a Java array, e.g. using the {@link MemorySegment#ofArray(int[])} factory;</li>
2213+
* <li>Segments obtained from a buffer, using the {@link MemorySegment#ofBuffer(Buffer)} factory; and</li>
2214+
* <li>Segments obtained from {@linkplain SymbolLookup#loaderLookup() loader lookup}.</li>
2215+
* </ul>
2216+
* If two memory segments are obtained from the same {@linkplain #ofBuffer(Buffer) buffer}
2217+
* or {@linkplain #ofArray(int[]) array}, the automatic scopes associated with said segments are considered
2218+
* {@linkplain #equals(Object) equal}, as the two segments have the same lifetime:
2219+
* {@snippet lang=java :
2220+
* byte[] arr = new byte[10];
2221+
* MemorySegment segment1 = MemorySegment.ofArray(arr);
2222+
* MemorySegment segment2 = MemorySegment.ofArray(arr);
2223+
* assert segment1.scope().equals(segment2.scope());
2224+
* }
22002225
*/
22012226
sealed interface Scope permits MemorySessionImpl {
22022227
/**

0 commit comments

Comments
 (0)