1 /* 2 * Copyright (c) 2022, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.foreign; 27 28 import jdk.internal.foreign.MemorySessionImpl; 29 import jdk.internal.javac.PreviewFeature; 30 import jdk.internal.ref.CleanerFactory; 31 32 import java.lang.foreign.MemorySegment.Scope; 33 34 /** 35 * An arena controls the lifecycle of native memory segments, providing both flexible allocation and timely deallocation. 36 * <p> 37 * An arena has a {@linkplain MemorySegment.Scope scope} - the <em>arena scope</em>. All the segments allocated 38 * by the arena are associated with the arena scope. As such, the arena determines the temporal bounds 39 * of all the memory segments allocated by it. 40 * <p> 41 * Moreover, an arena also determines whether access to memory segments allocated by it should be 42 * {@linkplain MemorySegment#isAccessibleBy(Thread) restricted} to specific threads. 43 * An arena is a {@link SegmentAllocator} and features several allocation methods that can be used by clients 44 * to obtain native segments. 45 * <p> 46 * The simplest arena is the {@linkplain Arena#global() global arena}. The global arena 47 * features an <em>unbounded lifetime</em>. As such, native segments allocated with the global arena are always 48 * accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the 49 * global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. 50 * {@snippet lang = java: 51 * MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()' 52 * ... 53 * // segment is never deallocated! 54 *} 55 * <p> 56 * Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena 57 * which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. As such, the regions 58 * of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time 59 * <em>after</em> the automatic arena (and all the segments allocated by it) becomes 60 * <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below: 61 * {@snippet lang = java: 62 * MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()' 63 * ... 64 * segment = null; // the segment region becomes available for deallocation after this point 65 *} 66 * Memory segments allocated with an automatic arena can also be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. 67 * <p> 68 * Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over 69 * the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this, 70 * namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature 71 * bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined 72 * arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory 73 * backing memory segments allocated with a confined arena are deallocated when the confined arena is closed. 74 * When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access 75 * operations on these segments will fail {@link IllegalStateException}: 76 * 77 * {@snippet lang = java: 78 * MemorySegment segment = null; 79 * try (Arena arena = Arena.ofConfined()) { // @highlight regex='ofConfined()' 80 * segment = arena.allocate(100); 81 * ... 82 * } // segment region deallocated here 83 * segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException 84 *} 85 * 86 * Memory segments allocated with a {@linkplain #ofConfined() confined arena} can only be accessed (and closed) by the 87 * thread that created the arena. If access to a memory segment from multiple threads is required, clients can allocate 88 * segments in a {@linkplain #ofShared() shared arena} instead. 89 * <p> 90 * The characteristics of the various arenas are summarized in the following table: 91 * 92 * <blockquote><table class="plain"> 93 * <caption style="display:none">Arenas characteristics</caption> 94 * <thead> 95 * <tr> 96 * <th scope="col">Kind</th> 97 * <th scope="col">Bounded lifetime</th> 98 * <th scope="col">Explicitly closeable</th> 99 * <th scope="col">Accessible from multiple threads</th> 100 * </tr> 101 * </thead> 102 * <tbody> 103 * <tr><th scope="row" style="font-weight:normal">Global</th> 104 * <td style="text-align:center;">No</td> 105 * <td style="text-align:center;">No</td> 106 * <td style="text-align:center;">Yes</td></tr> 107 * <tr><th scope="row" style="font-weight:normal">Automatic</th> 108 * <td style="text-align:center;">Yes</td> 109 * <td style="text-align:center;">No</td> 110 * <td style="text-align:center;">Yes</td></tr> 111 * <tr><th scope="row" style="font-weight:normal">Confined</th> 112 * <td style="text-align:center;">Yes</td> 113 * <td style="text-align:center;">Yes</td> 114 * <td style="text-align:center;">No</td></tr> 115 * <tr><th scope="row" style="font-weight:normal">Shared</th> 116 * <td style="text-align:center;">Yes</td> 117 * <td style="text-align:center;">Yes</td> 118 * <td style="text-align:center;">Yes</td></tr> 119 * </tbody> 120 * </table></blockquote> 121 * 122 * <h2 id = "thread-confinement">Safety and thread-confinement</h2> 123 * 124 * Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed 125 * <em>after</em> the arena has been closed. The cost of providing this guarantee varies based on the 126 * number of threads that have access to the memory segments allocated by the arena. For instance, if an arena 127 * is always created and closed by one thread, and the memory segments allocated by the arena are always 128 * accessed by that same thread, then ensuring correctness is trivial. 129 * <p> 130 * Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed 131 * by a thread other than the accessing thread, then ensuring correctness is much more complex. For example, a segment 132 * allocated with the arena might be accessed <em>while</em> another thread attempts, concurrently, to close the arena. 133 * To provide the strong temporal safety guarantee without forcing every client, even simple ones, to incur a performance 134 * impact, arenas are divided into <em>thread-confined</em> arenas, and <em>shared</em> arenas. 135 * <p> 136 * Confined arenas, support strong thread-confinement guarantees. Upon creation, they are assigned an 137 * <em>owner thread</em>, typically the thread which initiated the creation operation. 138 * The segments created by a confined arena can only be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} 139 * by the owner thread. Moreover, any attempt to close the confined arena from a thread other than the owner thread will 140 * fail with {@link WrongThreadException}. 141 * <p> 142 * Shared arenas, on the other hand, have no owner thread. The segments created by a shared arena 143 * can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. This might be useful when 144 * multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing). 145 * Moreover, a shared arena can be closed by any thread. 146 * 147 * <h2 id = "custom-arenas">Custom arenas</h2> 148 * 149 * Clients can define custom arenas to implement more efficient allocation strategies, or to have better control over 150 * when (and by whom) an arena can be closed. As an example, the following code defines a <em>slicing arena</em> that behaves 151 * like a confined arena (i.e., single-threaded access), but internally uses a 152 * {@linkplain SegmentAllocator#slicingAllocator(MemorySegment) slicing allocator} to respond to allocation requests. 153 * When the slicing arena is closed, the underlying confined arena is also closed; this will invalidate all segments 154 * allocated with the slicing arena (since the scope of the slicing arena is the same as that of the underlying 155 * confined arena): 156 * 157 * {@snippet lang = java: 158 * class SlicingArena implements Arena { 159 * final Arena arena = Arena.ofConfined(); 160 * final SegmentAllocator slicingAllocator; 161 * 162 * SlicingArena(long size) { 163 * slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); 164 * } 165 * 166 * public MemorySegment allocate(long byteSize, long byteAlignment) { 167 * return slicingAllocator.allocate(byteSize, byteAlignment); 168 * } 169 * 170 * public MemorySegment.Scope scope() { 171 * return arena.scope(); 172 * } 173 * 174 * public void close() { 175 * arena.close(); 176 * } 177 * 178 * } 179 * } 180 * 181 * In other words, a slicing arena provides a vastly more efficient and scalable allocation strategy, while still retaining 182 * the timely deallocation guarantee provided by the underlying confined arena: 183 * 184 * {@snippet lang = java: 185 * try (Arena slicingArena = new SlicingArena(1000)) { 186 * for (int i = 0; i < 10; i++) { 187 * MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); 188 * ... 189 * } 190 * } // all memory allocated is released here 191 * } 192 * 193 * @implSpec 194 * Implementations of this interface are thread-safe. 195 * 196 * @see MemorySegment 197 * 198 * @since 20 199 */ 200 @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) 201 public interface Arena extends SegmentAllocator, AutoCloseable { 202 203 /** 204 * Creates a new arena that is managed, automatically, by the garbage collector. 205 * Segments allocated with the returned arena can be 206 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. 207 * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. 208 * 209 * @return a new arena that is managed, automatically, by the garbage collector. 210 */ 211 static Arena ofAuto() { 212 return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()).asArena(); 213 } 214 215 /** 216 * Obtains the global arena. Segments allocated with the global arena can be 217 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. 218 * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}. 219 * 220 * @return the global arena. 221 */ 222 static Arena global() { 223 class Holder { 224 static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena(); 225 } 226 return Holder.GLOBAL; 227 } 228 229 /** 230 * {@return a new confined arena} Segments allocated with the confined arena can be 231 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by the thread that created the arena, 232 * the arena's <em>owner thread</em>. 233 */ 234 static Arena ofConfined() { 235 return MemorySessionImpl.createConfined(Thread.currentThread()).asArena(); 236 } 237 238 /** 239 * {@return a new shared arena} Segments allocated with the global arena can be 240 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. 241 */ 242 static Arena ofShared() { 243 return MemorySessionImpl.createShared().asArena(); 244 } 245 246 /** 247 * Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes). 248 * The returned segment is associated with this {@linkplain #scope() arena scope}. 249 * The segment's {@link MemorySegment#address() address} is the starting address of the 250 * allocated off-heap region of memory backing the segment, and the address is 251 * aligned according the provided alignment constraint. 252 * 253 * @implSpec 254 * Implementations of this method must return a native segment featuring the requested size, 255 * and that is compatible with the provided alignment constraint. Furthermore, for any two segments 256 * {@code S1, S2} returned by this method, the following invariant must hold: 257 * 258 * {@snippet lang = java: 259 * S1.asOverlappingSlice(S2).isEmpty() == true 260 * } 261 * 262 * @param byteSize the size (in bytes) of the off-heap region of memory backing the native memory segment. 263 * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment. 264 * @return a new native memory segment. 265 * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code byteAlignment <= 0}, or if {@code byteAlignment} 266 * is not a power of 2. 267 * @throws IllegalStateException if this arena has already been {@linkplain #close() closed}. 268 * @throws WrongThreadException if this arena is confined, and this method is called from a thread 269 * other than the arena's owner thread. 270 */ 271 @Override 272 default MemorySegment allocate(long byteSize, long byteAlignment) { 273 return ((MemorySessionImpl)scope()).allocate(byteSize, byteAlignment); 274 } 275 276 /** 277 * {@return the arena scope} 278 */ 279 Scope scope(); 280 281 /** 282 * Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain Scope#isAlive() alive}, 283 * and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the 284 * segments obtained from this arena are also released. 285 * 286 * @apiNote This operation is not idempotent; that is, closing an already closed arena <em>always</em> results in an 287 * exception being thrown. This reflects a deliberate design choice: failure to close an arena might reveal a bug 288 * in the underlying application logic. 289 * 290 * @implSpec If this method completes normally, then {@code this.scope().isAlive() == false}. 291 * Implementations are allowed to throw {@link UnsupportedOperationException} if an explicit close operation is 292 * not supported. 293 * 294 * @see Scope#isAlive() 295 * 296 * @throws IllegalStateException if the arena has already been closed. 297 * @throws IllegalStateException if a segment associated with this arena is being accessed concurrently, e.g. 298 * by a {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}. 299 * @throws WrongThreadException if this arena is confined, and this method is called from a thread 300 * other than the arena's owner thread. 301 * @throws UnsupportedOperationException if this arena cannot be closed explicitly. 302 */ 303 @Override 304 void close(); 305 306 }