-
Notifications
You must be signed in to change notification settings - Fork 61
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
8281855: Rename ResourceScope to MemorySession #641
Conversation
👋 Welcome back mcimadamore! A progress list of the required criteria for merging this PR into |
* accessors (see {@link MemorySegment#session()}). This class forwards all session methods to the underlying | ||
* "root" session implementation, and throws {@link UnsupportedOperationException} on close. | ||
*/ | ||
public final static class NonCloseableView implements MemorySession, Scoped { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simple wrapper class around a session which delegates everything to the session from which it is built - and overrides close
to throw an exception. I believe the cost for creating these little instances will be negligible - if problems show up, we can always move to more complex approaches (e.g. where MemorySegment
implements MemorySession
, or where we use @Stable
fields to cache a non-closeable view inside the root memory session).
|
||
public interface Scoped { | ||
ResourceScope scope(); | ||
MemorySessionImpl sessionImpl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is used to retrieve the "root" session associated with either a resource, or a session view. This means that, to simplify the implementation, MemorySessionImpl
has to implement Scoped
as well (and return itself).
I've fixed javadoc references to "scopes" and improved the javadoc text in several places. A link to a javadoc corresponding to these changes can be found here: |
Webrevs
|
Mailing list message from Maurizio Cimadamore on panama-dev: That would be an implicit session. Implicit sessions are back to being Maurizio |
*/ | ||
ResourceScope scope(); | ||
MemorySession session(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, could we take advantage of the type system and return a MemorySessionView which does not have a close method on it. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a possibility which we have explored. In the end we decided against it because we felt splitting session/scope in two would have had a negative effect on API discoverability. Note that non-closeable sessions are not just used for e.g. MemorySegment::session
, but they are also returned by MemorySession::global
and MemorySession::openImplicit
. If we ever do structured sessions (e.g. session that give access to any threads that inherit a given Loom scope local), then it is likely that these sessions will be non-closeable too.
Also, you would need to replace most API methods accepting MemorySession with MemorySessionView - and add a subtyping relationhip between MemorySessionView and MemorySession (so that you can pass a MemorySession where a view is expected). Ultimately that all felt a bit too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimately that all felt a bit too much.
To be clear, it felt too much not because the refactoring was complex (it was not) but because this creates a problem of how do you name the non-closeable entity (MemorySessionView
is fine when returned by MemorySegment::session
, less fine when accepted by MemorySegment::allocateNative
). And if you give it a more general name, then you end up with two abstractions fighting for API visibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, rethinking about it, the reason to add a session
accessor to MemorySegment
was to properly bind the lifetime of an allocated segment to an existing one and to correctly observe that lifetime. It sounds like the functionality of an allocator to me. Maybe we could instead return a SegmentAllocator
instead (and change MemorySession
to SegmentDeallocator
probably) and extend the functionality of a SegmentAllocator
to check the validity of its associating MemorySession
. Even if it is not the case we would need to check the state of a SegmentAllocator
anyway to ensure that we can allocate memory with it.
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That option was also considered, but discarded because it would make SegmentAllocator
stateful, e.g. more complex than just a functional interface. Right now it is pretty easy to define a new allocator - but if the allocator has to track lifetime, it becomes harder to define custom ones. Also, saying that, to create an upcall stub you need a SegmentAllocator
, or that to create an unsafe segment view of a memory address (MemorySegment::ofAddress
) you need an allocator, is a bit odd - in the sense that you don't really need allocation capabilities there - you just need some kind of lifetime abstraction.
So, I think the "allocation" angle is valid in most cases, but I think that it only works well for native segments and doesn't really generalize to all segments - unsafe views, memory mapped segments, heap segments, etc.
I think separating lifecycle from allocation was a really powerful move, which paid off considerably. Having a single allocator abstraction perform double duty seems a step backwards IMHO.
I see, rethinking about it, the reason to add a
session
accessor toMemorySegment
was to properly bind the lifetime of an allocated segment to an existing one and to correctly observe that lifetime. It sounds like the functionality of an allocator to me. Maybe we could instead return aSegmentAllocator
instead (and changeMemorySession
toSegmentDeallocator
probably) and extend the functionality of aSegmentAllocator
to check the validity of its associatingMemorySession
. Even if it is not the case we would need to check the state of aSegmentAllocator
anyway to ensure that we can allocate memory with it. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hat option was also considered, but discarded because it would make
SegmentAllocator
stateful, e.g. more complex than just a functional interface.
Note that today there is no contract which says that all segments returned by a SegmentAllocator
must have the same session. While some allocators might behave like that, I think it would not be ok to force that behavior on all allocators. And, we considered to add, perhaps in the future, some small subinterface, like ScopedSegmentAllocator
which also has a session
accessor - this would be a good return type for the arena allocator methods, for instance. But this is a move that is orthogonal to the changes described in this PR (and also not a binding one - we can add the sub-interface even at a later point w/o breaking compatibility).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for your clarification, I still think that having a scope that throw on close()
sounds a lot like the problem with UnmodifiableCollection
but it seems the alternatives have been considered already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed the issues are similar. However, there are some characteristics that are unique to this API. MemorySegment is a lower-level API than, say Collection. Also, while we currently do not generate method handles which require a MemorySession
, adding intricate subtyping hierarchies always has a cost when interacting with invokeExact
- so this API often errs on the side of "avoid unnecessary intermediate types, if we can avoid it". Not to dismiss your concern, which is a valid one - just wanted to explain some of the rationale behind some of the API decisions we've been doing.
src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
Outdated
Show resolved
Hide resolved
@mcimadamore this pull request can not be integrated into git checkout session_preview
git fetch https://git.openjdk.java.net/panama-foreign foreign-preview
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge foreign-preview"
git push |
…late Co-authored-by: Paul Sandoz <paul.d.sandoz@googlemail.com>
Co-authored-by: Paul Sandoz <paul.d.sandoz@googlemail.com>
* replace spurious usages of `scope` in direct buffer template
@mcimadamore This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been no new commits pushed to the ➡️ To integrate this PR with the above commit message to the |
src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java
Outdated
Show resolved
Hide resolved
@Override | ||
public void cleanup() { | ||
freeUpcallStub(entry); | ||
} | ||
}); | ||
return new NativeSymbolImpl("upcall:" + Long.toHexString(entry), MemoryAddress.ofLong(entry), scope); | ||
return new NativeSymbolImpl("upcall:" + Long.toHexString(entry), MemoryAddress.ofLong(entry), ((MemorySessionImpl)session)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, this doesn't use Scoped.toSessionImpl
since the session doesn't need to be closed in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No: NativeSymbolImpl
wants a true, closeable MemorySessionImpl. Then, if you look at that class, its session
method will return a non closeable view of that session. The toSessionImpl
happens in AbstractCLinker::upcallStub
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I think that's fine, but maybe that call should be moved here for clarity? (or maybe just add an assert session.isClosable()
for documentation).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, the comment helps :)
src/java.base/share/classes/java/lang/foreign/MemorySession.java
Outdated
Show resolved
Hide resolved
private static final Function<Consumer<VaList.Builder>, VaList> winVaListFactory | ||
= actions -> Windowsx64Linker.newVaList(actions, ResourceScope.newConfinedScope()); | ||
= actions -> Windowsx64Linker.newVaList(actions, MemorySession.openConfined()); | ||
private static final Function<Consumer<VaList.Builder>, VaList> sysvVaListFactory | ||
= actions -> SysVx64Linker.newVaList(actions, ResourceScope.newConfinedScope()); | ||
= actions -> SysVx64Linker.newVaList(actions, MemorySession.openConfined()); | ||
private static final Function<Consumer<VaList.Builder>, VaList> linuxAArch64VaListFactory | ||
= actions -> LinuxAArch64Linker.newVaList(actions, ResourceScope.newConfinedScope()); | ||
= actions -> LinuxAArch64Linker.newVaList(actions, MemorySession.openConfined()); | ||
private static final Function<Consumer<VaList.Builder>, VaList> macAArch64VaListFactory | ||
= actions -> MacOsAArch64Linker.newVaList(actions, ResourceScope.newConfinedScope()); | ||
= actions -> MacOsAArch64Linker.newVaList(actions, MemorySession.openConfined()); | ||
private static final Function<Consumer<VaList.Builder>, VaList> platformVaListFactory | ||
= (builder) -> VaList.make(builder, ResourceScope.newConfinedScope()); | ||
|
||
private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> winVaListScopedFactory | ||
= (builder, scope) -> Windowsx64Linker.newVaList(builder, scope); | ||
private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> sysvVaListScopedFactory | ||
= (builder, scope) -> SysVx64Linker.newVaList(builder, scope); | ||
private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> linuxAArch64VaListScopedFactory | ||
= (builder, scope) -> LinuxAArch64Linker.newVaList(builder, scope); | ||
private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> macAArch64VaListScopedFactory | ||
= (builder, scope) -> MacOsAArch64Linker.newVaList(builder, scope); | ||
private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> platformVaListScopedFactory | ||
= (builder, scope) -> VaList.make(builder, scope); | ||
= (builder) -> VaList.make(builder, MemorySession.openConfined()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that the tests no longer close the scope, maybe these scopes should be implicit scopes? (at least then we still test the eventual closure of the VaList).
test/micro/org/openjdk/bench/java/lang/foreign/BulkMismatchAcquire.java
Outdated
Show resolved
Hide resolved
/integrate |
Going to push as commit 9214de4. |
@mcimadamore Pushed as commit 9214de4. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
This patch implements some of the changes described in [1] and [2]. More specifically, the
ResourceScope
abstraction is renamed intoMemorySession
, and the session accessors inMemorySegment
,NativeSymbol
andVaList
are tweaked to return non-closeable session views. To counteract this change,MemorySession
now supportsequals
andhashCode
, so that sessions views can be compared to each other in ways that are not identity-sensitive.[1] - https://mail.openjdk.java.net/pipermail/panama-dev/2022-February/016152.html
[2] - https://mail.openjdk.java.net/pipermail/panama-dev/2022-February/016254.html
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/panama-foreign pull/641/head:pull/641
$ git checkout pull/641
Update a local copy of the PR:
$ git checkout pull/641
$ git pull https://git.openjdk.java.net/panama-foreign pull/641/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 641
View PR using the GUI difftool:
$ git pr show -t 641
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/panama-foreign/pull/641.diff