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

8301228: Add more ways to resize zero-length memory segments #775

Closed

Conversation

mcimadamore
Copy link
Collaborator

@mcimadamore mcimadamore commented Jan 26, 2023

When dealing with native libraries, it is not uncommon to obtain zero-length memory segments. Since the size of these segment is zero (for safety reasons), clients need to be able to resize these segments.

At the time of writing, there are two ways to resize zero-length memory segment:

  • By calling MemorySegment.ofAddress, and obtaining a new segment with desired base address, size and lifetime.
  • By using an "unbounded" layout (e.g. OfAddress.asUnbounded()) in order to perform address dereference.

Both approaches can be improved. First, while MemorySegment.ofAddress is a good primitive to create truly custom native segments, sometimes the only thing a client wants to do is to quickly be able to resize the segment to the desired size.

Secondly, while unbounded address layouts are useful, there are cases where the size of the dereferenced segment is known statically. It is a bit sad that the API does not let clients to specify a bounded size to be specified for a given address layout (as that would lead to safer code). Of course, in some cases the size would still be unknonw statically, so there has to be a way to go unbounded, if needed.

This patch fixes rectifies the situation in two ways:

  • by adding a method, namely MemorySegment::asUnbounded() which allows to obtain a view of a native segment with maximal size (i.e. Long.MAX_VALUE). Note that clients can first use this method, and then use regular slicing methods to further restrict the size, as required.
  • by replacing the OfAddress::asUnbounded() method with a more targeted OfAddress::withTargetLayout(MemoryLayout). Note that the unbounded behavior can still be obtained by passing something like MemoryLayout.sequenceLayout(JAVA_BYTE).

When working on the patch we have considered other options, such as that of adding unsafe slicing methods which can be thought of as the composition of asUnbounded with some other (safe) slicing method. And, whether or not to keep OfAddress::asUnbounded (as a shortcut).

I would like to start from the more principled approach in this patch. Since the methods we are talking about are all restricted, there is a certain appeal in trying to keep their number limited. And, duplicating all the slicing methods into new restricted variants can increase the footprint of the MemorySegment API for a relatively little gain. That said, other overloads can always be added as required, at a later point.

Note that this patch also adds more safe slicing variants, namely:

  • asSlice(long offset, MemoryLayout)
  • asSlice(long offset, long newSize, long byteAlignment)

The former is very handy when resizing an unbounded segment to a given layout (e.g. JAVA_INT). Since it takes a layout parameter, it makes sense to check if the resulting slice conforms to the alignment constraint specified by the memory layout. The second method is basically the "real" slicing primitive - which does resizing, offset and alignment check. All other slicing methods can be explained in terms of this.

Interestingly, thanks to the new slicing methods, we can now more easily deal with alignment checks from slicingAllocator and prefixAllocator.

Another note: for the time being, MemorySegment::asUnbounded will only work on native segments. While the implementation could be easily made total, I'm a bit skeptical of allowing out-of-bound access from a Java array :-)


Progress

  • Change must not contain extraneous whitespace
  • Change must be properly reviewed (1 review required, with at least 1 Committer)

Issue

  • JDK-8301228: Add more ways to resize zero-length memory segments

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/panama-foreign pull/775/head:pull/775
$ git checkout pull/775

Update a local copy of the PR:
$ git checkout pull/775
$ git pull https://git.openjdk.org/panama-foreign pull/775/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 775

View PR using the GUI difftool:
$ git pr show -t 775

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/panama-foreign/pull/775.diff

Sorry, something went wrong.

@bridgekeeper
Copy link

bridgekeeper bot commented Jan 26, 2023

👋 Welcome back mcimadamore! A progress list of the required criteria for merging this PR into foreign-memaccess+abi will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

- Address layout should display target layout if present (e.g. `a64:i32`)
- Sequence layout should not display element size if maximal (e.g. `*:i32`)
Copy link
Member

@JornVernee JornVernee left a comment

Choose a reason for hiding this comment

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

Some initial comments

mcimadamore and others added 6 commits January 26, 2023 19:15
Fix bug in how BoxAddress binding is called
Co-authored-by: Jorn Vernee <JornVernee@users.noreply.github.com>
Co-authored-by: Jorn Vernee <JornVernee@users.noreply.github.com>
Co-authored-by: Jorn Vernee <JornVernee@users.noreply.github.com>
Co-authored-by: Jorn Vernee <JornVernee@users.noreply.github.com>
Co-authored-by: Jorn Vernee <JornVernee@users.noreply.github.com>
@Test(dataProvider = "layoutsAndAlignments")
public void testNativeUpcallArg(long alignment, ValueLayout layout) throws Throwable {
boolean badAlign = layout.byteAlignment() > alignment;
if (badAlign) return; // this will crash the JVM (exception occurs when going into the upcall stub)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Any idea on this @JornVernee ? When the alignment exception is thrown (this happens when we execute the box address bindings before entering the upcall stub), the JVM exits, reporting an exception that points to where the downcall is made, which is quite confusing. I still think the check is worthwhile having (after all a layout is more than just the size), it's a bit unfortunate that in the case of upcalls a failed check ends up terminating the JVM (but then again, if you are really expecting a pointer with certain characteristics, and you are given another - you might just cross fingers and hope it would be ok).

Copy link
Member

Choose a reason for hiding this comment

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

Right, an exception thrown outside of user code will go to the uncaught exception handler, and exit the VM after printing a stack trace. (Though, the stack trace should point at the wrapper class generated by the binding specializer, not the downcall call site. Does this change if you use -XX:+ShowHiddenFrames?)

I guess it could be problematic if an upcall has some 'optional' pointer argument, that is sometimes garbage (as opposed to NULL). But, a user can also create an address layout with an unaligned target to avoid the exception if needed.

If the pointer you receive is not supposed to be garbage, a mis-aligned pointer might mean you were given a corrupt pointer. So in that case the exception seems useful.

Either way, I think there is little reason why someone might want to try to catch such an exception, since the code would be in the middle of boxing arguments. There seems to be no way to sanely recover in that situation. (except maybe for doing some additional logging or something, but I think we could solve that by integrating our uncaught exception handler with the one installed on the thread. Or one installed through a linker option).

If you still want to test the negative case (which I think we probably should), I suggest adding a new class that extends UpcallTestHelper and use runInNewProcess to run the test code, then check stderr for the exception message. See e.g. TestPassHeapSegment::testNoHeapReturns which also tests an exception thrown when processing the bindings.

@mcimadamore mcimadamore marked this pull request as ready for review January 27, 2023 11:17
@mcimadamore mcimadamore changed the title Rework support for unbounded address layouts. 8301228: Add more ways to resize zero-length memory segments Jan 27, 2023
@openjdk openjdk bot added the rfr Ready for review label Jan 27, 2023
@mlbridge
Copy link

mlbridge bot commented Jan 27, 2023

Webrevs

@Test(dataProvider = "layoutsAndAlignments")
public void testNativeUpcallArg(long alignment, ValueLayout layout) throws Throwable {
boolean badAlign = layout.byteAlignment() > alignment;
if (badAlign) return; // this will crash the JVM (exception occurs when going into the upcall stub)
Copy link
Member

Choose a reason for hiding this comment

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

Right, an exception thrown outside of user code will go to the uncaught exception handler, and exit the VM after printing a stack trace. (Though, the stack trace should point at the wrapper class generated by the binding specializer, not the downcall call site. Does this change if you use -XX:+ShowHiddenFrames?)

I guess it could be problematic if an upcall has some 'optional' pointer argument, that is sometimes garbage (as opposed to NULL). But, a user can also create an address layout with an unaligned target to avoid the exception if needed.

If the pointer you receive is not supposed to be garbage, a mis-aligned pointer might mean you were given a corrupt pointer. So in that case the exception seems useful.

Either way, I think there is little reason why someone might want to try to catch such an exception, since the code would be in the middle of boxing arguments. There seems to be no way to sanely recover in that situation. (except maybe for doing some additional logging or something, but I think we could solve that by integrating our uncaught exception handler with the one installed on the thread. Or one installed through a linker option).

If you still want to test the negative case (which I think we probably should), I suggest adding a new class that extends UpcallTestHelper and use runInNewProcess to run the test code, then check stderr for the exception message. See e.g. TestPassHeapSegment::testNoHeapReturns which also tests an exception thrown when processing the bindings.

/*
* @test
* @enablePreview
* @requires os.arch=="amd64" | os.arch=="x86_64" | os.arch=="aarch64" | os.arch=="riscv64"
Copy link
Member

Choose a reason for hiding this comment

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

AFAIK this requires also needs a check for sun.arch.data.model == "64" as the linker doesn't work on 32-bit VMs. (could still be running a 32-bit VM on an amd64 machine)

Fix javadoc for restricted method MemorySegment::asUnbounded
Add negative test for unaligned address in upcall
@openjdk
Copy link

openjdk bot commented Jan 27, 2023

@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:

8301228: Add more ways to resize zero-length memory segments

Reviewed-by: jvernee

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 117 new commits pushed to the foreign-memaccess+abi branch:

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the foreign-memaccess+abi branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Ready to be integrated label Jan 27, 2023
@mcimadamore
Copy link
Collaborator Author

/integrate

@openjdk
Copy link

openjdk bot commented Jan 27, 2023

Going to push as commit add0cf9.
Since your change was applied there have been 117 commits pushed to the foreign-memaccess+abi branch:

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Jan 27, 2023
@openjdk openjdk bot closed this Jan 27, 2023
@openjdk openjdk bot removed ready Ready to be integrated rfr Ready for review labels Jan 27, 2023
@openjdk
Copy link

openjdk bot commented Jan 27, 2023

@mcimadamore Pushed as commit add0cf9.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integrated Pull request has been integrated
Development

Successfully merging this pull request may close these issues.

None yet

2 participants