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