Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8296417: Make memory session a pure lifetime abstraction #750

Closed
121 changes: 121 additions & 0 deletions src/java.base/share/classes/java/lang/foreign/Arena.java
@@ -0,0 +1,121 @@
/*
* 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.ArenaImpl;
import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.javac.PreviewFeature;

/**
* An arena allocates and manages the lifecycle of native segments.
* <p>
* An arena is a {@linkplain AutoCloseable closeable} segment allocator that is associated with a {@link #session() memory session}.
* This session is created with the arena, and is closed when the arena is {@linkplain #close() closed}.
* Furthermore, all the native segments {@linkplain #allocate(long, long) allocated} by the arena are associated
* with that session.
* <p>
* The <a href="MemorySession.html#thread-confinement">confinement properties</a> of the session associated with an
* arena are determined by the factory used to create the arena. For instance, an arena created with {@link #openConfined()}
* is associated with a <em>confined</em> memory session. Conversely, an arena created with {@link #openShared()} is
* associated with a <em>shared</em> memory session.
* <p>
* An arena is 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.session());
* ...
* } // memory released here
*}
*
* @since 20
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface Arena extends SegmentAllocator, AutoCloseable permits ArenaImpl { //@@@: This can also be non-sealed!
mcimadamore marked this conversation as resolved.
Show resolved Hide resolved

/**
* Creates a native memory segment with the given size (in bytes), alignment constraint (in bytes) associated with
* this memory session. The {@link MemorySegment#address()} of the returned memory segment is the starting address of
mcimadamore marked this conversation as resolved.
Show resolved Hide resolved
* the newly allocated off-heap memory region backing the segment. Moreover, the {@linkplain MemorySegment#address() address}
* of the returned segment will be aligned according the provided alignment constraint.
* <p>
* Clients are responsible for ensuring that this arena is closed when the segments returned by this method are no
* longer in use. Failure to do so will result in off-heap memory leaks.
* <p>
* This is equivalent to the following code:
mcimadamore marked this conversation as resolved.
Show resolved Hide resolved
* {@snippet lang=java :
* MemorySegment.allocateNative(bytesSize, byteAlignment, session());
* }
* <p>
* The region of off-heap memory backing the returned native memory segment is initialized to zero.
*
* @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 session associated with this arena is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#isOwnedBy(Thread) owning} the session associated with this arena.
* @see MemorySegment#allocateNative(long, long, MemorySession)
*/
@Override
MemorySegment allocate(long byteSize, long byteAlignment);

/**
* {@return the session associated with this arena}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, curly bracers around @return

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the inline javadoc return tag?

*/
MemorySession session();

/**
* Closes this arena. This closes the {@linkplain #session() session} associated with this arena and invalidates
* all the memory segments associated with it. Any off-heap region of memory backing the segments associated with
* that memory session are also released.
* @throws IllegalStateException if the session associated with this arena is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#isOwnedBy(Thread) owning} the session associated with this arena.
*/
@Override
void close();

/**
* Creates a new arena, associated with a new confined session.
* @return a new arena, associated with a new confined session.
*/
static Arena openConfined() {
return new ArenaImpl(MemorySessionImpl.createConfined(Thread.currentThread()));
}

/**
* Creates a new arena, associated with a new shared session.
* @return a new arena, associated with a new shared session.
*/
static Arena openShared() {
return new ArenaImpl(MemorySessionImpl.createShared());
}
}
8 changes: 4 additions & 4 deletions src/java.base/share/classes/java/lang/foreign/Linker.java
Expand Up @@ -106,8 +106,8 @@
* <li>if {@code L} is a {@link GroupLayout}, then {@code C} is set to {@code MemorySegment.class}</li>
* </ul>
* Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other
* downcall method handles and, when no longer required, they can be {@linkplain MemorySession#close() released},
* via their associated {@linkplain MemorySession memory session}.
* downcall method handles and, when no longer required, they can be closed, via their associated
* {@linkplain MemorySession memory session}.
*
* <h2 id="safety">Safety considerations</h2>
*
Expand All @@ -121,7 +121,7 @@
* <ul>
* <li>The memory session of {@code A} is {@linkplain MemorySession#isAlive() alive}. Otherwise, the invocation throws
* {@link IllegalStateException};</li>
* <li>The invocation occurs in same thread as the one {@linkplain MemorySession#ownerThread() owning} the memory session of {@code R},
* <li>The invocation occurs in same thread as the one {@linkplain MemorySession#isOwnedBy(Thread) owning} the memory session of {@code R},
* if said session is confined. Otherwise, the invocation throws {@link WrongThreadException}; and</li>
* <li>The memory session of {@code R} is <em>kept alive</em> (and cannot be closed) during the invocation.</li>
*</ul>
Expand Down Expand Up @@ -274,7 +274,7 @@ default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor fun
* has a type that does not match the upcall stub <a href="Linker.html#upcall-stubs"><em>inferred type</em></a>.
* @throws IllegalStateException if {@code session} is not {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread
* {@linkplain MemorySession#ownerThread() owning} {@code session}.
* {@linkplain MemorySession#isOwnedBy(Thread) owning} {@code session}.
*/
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, MemorySession session);

Expand Down