mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8295044: Implementation of Foreign Function and Memory API (Second Preview)
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Reviewed-by: jvernee, pminborg, psandoz, alanb, sundar
This commit is contained in:
parent
bd381886e0
commit
73baadceb6
252 changed files with 9221 additions and 7889 deletions
166
src/java.base/share/classes/java/lang/foreign/Arena.java
Normal file
166
src/java.base/share/classes/java/lang/foreign/Arena.java
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.foreign;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
||||
/**
|
||||
* An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation.
|
||||
* <p>
|
||||
* An arena has a {@linkplain #scope() scope}, called the arena scope. When the arena is {@linkplain #close() closed},
|
||||
* the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}. As a result, all the
|
||||
* segments associated with the arena scope are invalidated, safely and atomically, their backing memory regions are
|
||||
* deallocated (where applicable) and can no longer be accessed after the arena is closed:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment segment = MemorySegment.allocateNative(100, arena.scope());
|
||||
* ...
|
||||
* } // memory released here
|
||||
*}
|
||||
*
|
||||
* Furthermore, an arena is a {@link SegmentAllocator}. All the segments {@linkplain #allocate(long, long) allocated} by the
|
||||
* arena are associated with the arena scope. This makes arenas extremely useful when interacting with foreign code, as shown below:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
* MemorySegment nativeString = arena.allocateUtf8String("Hello!");
|
||||
* MemorySegment upcallStub = linker.upcallStub(handle, desc, arena.scope());
|
||||
* ...
|
||||
* } // memory released here
|
||||
*}
|
||||
*
|
||||
* <h2 id = "thread-confinement">Safety and thread-confinement</h2>
|
||||
*
|
||||
* Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed
|
||||
* <em>after</em> the arena has been closed. The cost of providing this guarantee varies based on the
|
||||
* number of threads that have access to the memory segments allocated by the arena. For instance, if an arena
|
||||
* is always created and closed by one thread, and the memory segments associated with the arena's scope are always
|
||||
* accessed by that same thread, then ensuring correctness is trivial.
|
||||
* <p>
|
||||
* Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed
|
||||
* by a thread other than the accessing thread, then ensuring correctness is much more complex. For example, a segment
|
||||
* allocated with the arena might be accessed <em>while</em> another thread attempts, concurrently, to close the arena.
|
||||
* To provide the strong temporal safety guarantee without forcing every client, even simple ones, to incur a performance
|
||||
* impact, arenas are divided into <em>thread-confined</em> arenas, and <em>shared</em> arenas.
|
||||
* <p>
|
||||
* Confined arenas, support strong thread-confinement guarantees. Upon creation, they are assigned an
|
||||
* {@linkplain #isCloseableBy(Thread) owner thread}, typically the thread which initiated the creation operation.
|
||||
* The segments created by a confined arena can only be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed}
|
||||
* by the owner thread. Moreover, any attempt to close the confined arena from a thread other than the owner thread will
|
||||
* fail with {@link WrongThreadException}.
|
||||
* <p>
|
||||
* Shared arenas, on the other hand, have no owner thread. The segments created by a shared arena
|
||||
* can be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. This might be useful when
|
||||
* multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing).
|
||||
* Moreover, a shared arena {@linkplain #isCloseableBy(Thread) can be closed} by any thread.
|
||||
*
|
||||
* @since 20
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public interface Arena extends SegmentAllocator, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes).
|
||||
* The returned segment is associated with the arena scope.
|
||||
* The segment's {@link MemorySegment#address() address} is the starting address of the
|
||||
* allocated off-heap memory region backing the segment, and the address is
|
||||
* aligned according the provided alignment constraint.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation of this method is equivalent to the following code:
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment.allocateNative(bytesSize, byteAlignment, scope());
|
||||
*}
|
||||
* More generally implementations of this method must return a native segment featuring the requested size,
|
||||
* and that is compatible with the provided alignment constraint. Furthermore, for any two segments
|
||||
* {@code S1, S2} returned by this method, the following invariant must hold:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* S1.overlappingSlice(S2).isEmpty() == true
|
||||
*}
|
||||
*
|
||||
* @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment.
|
||||
* @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment.
|
||||
* @return a new native memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes}
|
||||
* is not a power of 2.
|
||||
* @throws IllegalStateException if the arena has already been {@linkplain #close() closed}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code scope().isAccessibleBy(T) == false}.
|
||||
* @see MemorySegment#allocateNative(long, long, SegmentScope)
|
||||
*/
|
||||
@Override
|
||||
default MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
return MemorySegment.allocateNative(byteSize, byteAlignment, scope());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the arena scope}
|
||||
*/
|
||||
SegmentScope scope();
|
||||
|
||||
/**
|
||||
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive},
|
||||
* and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the
|
||||
* segments associated with that scope are also released.
|
||||
*
|
||||
* @apiNote This operation is not idempotent; that is, closing an already closed arena <em>always</em> results in an
|
||||
* exception being thrown. This reflects a deliberate design choice: failure to close an arena might reveal a bug
|
||||
* in the underlying application logic.
|
||||
*
|
||||
* @see SegmentScope#isAlive()
|
||||
*
|
||||
* @throws IllegalStateException if the arena has already been closed.
|
||||
* @throws IllegalStateException if the arena scope is {@linkplain SegmentScope#whileAlive(Runnable) kept alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isCloseableBy(T) == false}.
|
||||
*/
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* {@return {@code true} if the provided thread can close this arena}
|
||||
* @param thread the thread to be tested.
|
||||
*/
|
||||
boolean isCloseableBy(Thread thread);
|
||||
|
||||
/**
|
||||
* {@return a new confined arena, owned by the current thread}
|
||||
*/
|
||||
static Arena openConfined() {
|
||||
return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new shared arena}
|
||||
*/
|
||||
static Arena openShared() {
|
||||
return MemorySessionImpl.createShared().asArena();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue