-
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
Improve NIO channel support for buffer views over segments #512
Improve NIO channel support for buffer views over segments #512
Conversation
👋 Welcome back chegar! A progress list of the required criteria for merging this PR into |
/contributor add pconcannon |
@ChrisHegarty |
@Override | ||
public Object acquireScope(Buffer targetBuffer, boolean async) { | ||
var targetScope = targetBuffer.scope(); | ||
if (targetScope == null || targetScope.isImplicit()) { |
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.
Hopefully some of this dance will not be necessary if we move to the proposed acquire/release API.
releasers.run(); | ||
} | ||
|
||
static record LinkedRunnable(Runnable node, Runnable next) |
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.
Nice - an alternative is to keep wrapping runnables, which is a trick we sometimes do. Of course you can risk running out of stack with that approach, which doesn't apply here.
@@ -382,7 +382,7 @@ default MemorySegment asSlice(MemoryAddress newBase) { | |||
* If this segment is associated with a shared scope, calling certain I/O operations on the resulting buffer might result in | |||
* an unspecified exception being thrown. Examples of such problematic operations are {@link FileChannel#read(ByteBuffer)}, | |||
* {@link FileChannel#write(ByteBuffer)}, {@link java.nio.channels.SocketChannel#read(ByteBuffer)} and | |||
* {@link java.nio.channels.SocketChannel#write(ByteBuffer)}. | |||
* {@link java.nio.channels.SocketChannel#write(ByteBuffer)}. TODO HERE Fix this comment |
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.
TODO comment here.
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 removed this reminder TODO, and updated the spec to refer to async I/O operations with byte buffer views over segments associated with a confined scope.
src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java
Outdated
Show resolved
Hide resolved
/** | ||
* Not a test, but infra for channel tests. | ||
*/ | ||
public class AbstractChannelsTest { |
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.
Many thanks for adding this (and other) tests. It's nice to see that now all combination of IO and scopes are tested - this should give us some confidence when fine tuning the impl.
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 still need to stabilise the tests on Windows, and expand out coverage for other async channel types, file, UDP, but that could be done in a follow-on PR.
A "lesson learned" in this PR seems to be that the AutoCloseable feature of ResourceScope.Handle is not as "hot" as it seemed. In fact, we don't seem to rely on that much (if at all). In part, I think, to avoid warnings; in part because the code just doesn't lend to it (e.g. handles are stored in fields). |
Webrevs
|
@Override | ||
public Scope.Handle acquireScope(Buffer buffer, boolean async) { | ||
var scope = buffer.scope(); | ||
if (scope == null || scope.isImplicit()) { |
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.
do you still need the null handling (both here and in IOUtil) ? We just return a singleton now for implicit scope, so I think you can make the code more regular w/o losing any performance.
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.
The null check is still needed - for "regular" buffers (that do not have a scope).
The isImplicit
check can probably be removed. It is an unproven mirco-optimization to avoid the bloat of chaining unnecessary handles. It could be removed, and dropped altogether or pushed into the IOUtil code that does the chaining ( do not create a new Releaser for a handle that we've seen before, I assume handles can be compared by identity? ) Maybe I'll just drop this, and revisit later when benchmarking.
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 wonder if it would make sense to use global scope in these cases. But yes, this is probably best tackled in a separate patch.
void checkValidState(); | ||
|
||
Thread ownerThread(); | ||
|
||
boolean isImplicit(); |
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.
sorry for the extra shuffling - this will all go away when we exit incubation ;-)
if (scope == null || scope.isImplicit()) { | ||
return null; | ||
} | ||
if (async && scope.ownerThread() != null) { |
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.
Crazy idea - are we sure we need this check? If user wants to really pass a confined buffer to an async IO, I belive a failure would still occur when the segment is accessed by a different thread - do you see reporting an error eagerly as something important? Will fibers change things so that e.g. we might be able to do more with a single thread?
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 think that there is scope for tweaking this in the future. At the moment with the current structure, the Windows async socket channel implementation will access the memory behind the scope's/segment's back ( through the long
address value that it retrieves ). It stores and reuses the "naked" address value from a thread other than that of the thread which initialed the I/O operation, all without invoking any memory access operations from Java. It's a non-trivial change to alter this, and not even clear if we should.
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.
Ugh- ok, I see that different implementation might not necessarily trigger a scope check...
src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/ResourceScope.java
Outdated
Show resolved
Hide resolved
src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java
Show resolved
Hide resolved
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'm not an expert of deep NIO socket code, but the proposed changes look sensible to me. Thus patch vastly expand usability of buffers obtained from memory segments, and it does so in a very minimalistic (but general) way.
@Override | ||
public Scope.Handle acquireScope(Buffer buffer, boolean async) { | ||
var scope = buffer.scope(); | ||
if (scope == null || scope.isImplicit()) { |
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 wonder if it would make sense to use global scope in these cases. But yes, this is probably best tackled in a separate patch.
if (scope == null || scope.isImplicit()) { | ||
return null; | ||
} | ||
if (async && scope.ownerThread() != null) { |
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.
Ugh- ok, I see that different implementation might not necessarily trigger a scope check...
@ChrisHegarty 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 |
Mailing list message from Pedro Lamarão on panama-dev: Em qui., 22 de abr. de 2021 ?s 11:24, Chris Hegarty <chegar at openjdk.java.net>
Consider also what the Windows API calls "registered I/O", also linux -- Antes de imprimir esta mensagem e seus anexos, certifique-se que seja |
1 similar comment
Mailing list message from Pedro Lamarão on panama-dev: Em qui., 22 de abr. de 2021 ?s 11:24, Chris Hegarty <chegar at openjdk.java.net>
Consider also what the Windows API calls "registered I/O", also linux -- Antes de imprimir esta mensagem e seus anexos, certifique-se que seja |
Correct, it's a detail of the implementation how the memory is ultimately accessed, and when (and for how long) the resource scope handle is acquired. So may vary for different implementations. The Windows asynchronous socket channels in the JDK use Overlapped I/O and registers to receive a notification via an I/O completion port. |
/integrate |
@ChrisHegarty Since your change was applied there has been 1 commit pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 8c53fef. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
Initial prototype changes to use resource scope handles when performing I/O operations with synchronous and asynchronous channels.
Further details can be found at: https://inside.java/2021/04/21/fma-and-nio-channels/
Progress
Reviewers
Contributors
<[email protected]>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/panama-foreign pull/512/head:pull/512
$ git checkout pull/512
Update a local copy of the PR:
$ git checkout pull/512
$ git pull https://git.openjdk.java.net/panama-foreign pull/512/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 512
View PR using the GUI difftool:
$ git pr show -t 512
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/panama-foreign/pull/512.diff