-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8360025: (se) Convert kqueue Selector Implementation to use FFM APIs #25546
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
Conversation
|
👋 Welcome back dclarke! A progress list of the required criteria for merging this PR into |
|
❗ This change is not yet ready to be integrated. |
|
@DarraghClarke The following labels will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command. |
| * questions. | ||
| */ | ||
|
|
||
| package jdk.internal.ffi.util; |
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 this is some general ffm utils for using ffm, you can put it in jdk.internal.foreign.
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.
These methods are only intended to be used for "panamization" and are not for general use. So, this is the reason we propose to have them here. But maybe some of them might be of general interest?
src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java
Outdated
Show resolved
Hide resolved
src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java
Outdated
Show resolved
Hide resolved
src/java.base/macosx/classes/jdk/internal/ffi/generated/kqueue/kqueue_h.java
Outdated
Show resolved
Hide resolved
| // filters | ||
| static final int EVFILT_READ = -1; | ||
| static final int EVFILT_WRITE = -2; | ||
| static final int EVFILT_READ = kqueue_h.EVFILT_READ(); |
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.
Could we remove these constants and use kqueue_h constants directly?
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, because those are private, accessible only through the public static getters.
| public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; | ||
| public static final AddressLayout C_POINTER = ValueLayout.ADDRESS | ||
| .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); | ||
| public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; |
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 int on Windows. So, we could perhaps use Linker.nativeLinker().canonicalLayouts().get("long") here? "size_t" and "wchar_t" are other canonical layouts that may differ across platforms.
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 this is a good idea and would allow us to make BindingUtils shared,
Though I wonder if it's best to move BindingUtils to be shared, or if its worth merging it into FFMUtils. I'd favour the second option
|
Some of the files are missing a blank line at the end. |
| private final int filter; | ||
| private final int maxEvents; | ||
| private final long address; | ||
| private final MemorySegment pollArrayRegions; |
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 poll array so I think very confusing to see "Regions" in the name.
| result = kqueue_h.kevent( | ||
| kqfd, keventMS, 1, NULL, | ||
| 0, NULL); | ||
| } while ((result == KQUEUE_ERROR_VALUE)); |
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 only case where the syscall should be restarted is EINTR.
| if (result == -errno_h.EINTR()) { | ||
| return IOStatus.INTERRUPTED; | ||
| } else { | ||
| throw new IOException("kqueue_poll failed"); |
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 syscall is "kqueue" rather than "kqueue_poll".
| static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.loaderLookup() | ||
| .or(Linker.nativeLinker().defaultLookup()); | ||
|
|
||
| public static void traceDowncall(String name, Object... args) { |
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 tracing downcal support is also something that might be moved directly to the Linker -- either as a Linker option, or as a global flag, similar to Xcheck:jni (not in this PR)
| System.out.printf("%s(%s)\n", name, traceArgs); | ||
| } | ||
|
|
||
| public static MemorySegment findOrThrow(String symbol) { |
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 this can go away once we update jextract to a new version that doesn't require this (not in this PR):
openjdk/jextract#280
|
I made a few changes mostly based on comments but I'll give a little patch notes:
I ran this against the selector tests on all OSs and everything seems green after repeated tests |
| int res = kqueue_h.kqueue(); | ||
| if (res < 0) { | ||
| throw ErrnoUtils.IOExceptionWithErrnoString(-res, | ||
| "kqueue failed"); | ||
| } | ||
| this.kqfd = res; |
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.
Maybe this duplicated check should probably be moved into a common method in KQueue, like the old KQueue.create() method, but implemented in Java:
static int create() throws IOException {
int res = kqueue_h.kqueue();
if (res < 0) {
throw ErrnoUtils.IOExceptionWithErrnoString(-res, "kqueue failed");
}
return res;
}| while (i < n) { | ||
| long keventAddress = KQueue.getEvent(address, i); | ||
| int fdVal = KQueue.getDescriptor(keventAddress); | ||
| MemorySegment eventMS = KQueue.getEvent(pollArray, i); |
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.
Nit: Can we call these variables eventSegment? My reptile part of the brain first read "event in milliseconds".
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.
Yes, I think rename this to kevent or keventSegment as it's a kevent struct (which is why the Unsafe usage named it keventAddres).
minborg
left a 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.
Some of the methods in FFMUtils are general and should be available on all platforms. I think we can fix this when we submit the next panamization PR.
|
|
||
| // address of the poll array passed to kqueue_wait | ||
| private final long address; | ||
| private final MemorySegment pollArrayRegions; |
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 "Regions" suffix is confusing here. This is the contiguous poll array when invoking kqueue so I don't think "Regions" should be in the name.
| @Override | ||
| protected int doSelect(Consumer<SelectionKey> action, long timeout) | ||
| throws IOException | ||
| throws IOException |
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 assume you didn't mean to change this (processEvents is another).
| * questions. | ||
| */ | ||
|
|
||
| package jdk.internal.ffi.generated; |
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.
Using jdk.internal.ffi.generated.** for jextract generated code is good. I'm not sure this is the right place for ErrnoUtils.
|
|
||
| private static final long ERRNO_STRING_HOLDER_ARRAY_SIZE = 256L; | ||
|
|
||
| public static IOException IOExceptionWithErrnoString(int errno, String message) { |
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 naming is a bit confusing here, minimally need a method description so we can quickly understand the relationship between "message" and strerror(errno).
| final class KQueue { | ||
|
|
||
| private static final Unsafe unsafe = Unsafe.getUnsafe(); | ||
| private static final BufferStack POOL = BufferStack.of(timespec.layout()); |
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 we'll need to rename this to BUFFER_STACK or something better as "POOL" is a bit confusing.
| static long getEvent(long address, int i) { | ||
| return address + (SIZEOF_KQUEUEEVENT*i); | ||
| static MemorySegment getEvent(MemorySegment memoryHandle, int i) { | ||
| return kevent.asSlice(memoryHandle, i); |
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.
Previous work eliminated allocating from the performance critical usages, might have to check the implications of asSlice 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.
We could use indices/offsets directly in the methods that receive the slices from this method.
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.
getDescriptor(MemorySegment keventArray, int index) and getFilter(MemorySegment keventArray, int index) would work for the use-sites.
The other thing here is that a fd is an int rather than a long. The re-implemented KQueue should pick ident or fd (the common usage). Right now register takes an int fd, where the modified getDescriptor is actually returning the 64-bit ident.
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.
So on my local branch I've address all the comments except the two in this thread,
So for using the indices directly what would that look like? would we remove getEvent and instead pass the MemorySegment and whatever indices we want to access directly to getDescriptor/getFilter?
As for fd/ident, I think that's interesting, the reason for getDescriptor using long ident is because that's what jextract generated for it (from uintptr_t specifically), I see that in mainline today it's just the simpler call of return unsafe.getInt(address + OFFSET_IDENT); I could try just replicating the existing code instead of using the generated method?
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 could try just replicating the existing code instead of using the generated method?
We always use the ident as an fd so don't use the full width. It doesn't matter if KQueue uses int fd or long ident in the API that it exposes but I'd prefer not to have a mix of both, at least not with good naming to avoid mis-use. If you want a preference then expose just the narrow fd form as this would work best the use-sites.
| private static native int flagsOffset(); | ||
|
|
||
| static native int create() throws IOException; | ||
| static public int register(int kqfd, int fd, int filter, int flags) { |
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 need to make this public.
| static native int create() throws IOException; | ||
| static public int register(int kqfd, int fd, int filter, int flags) { | ||
| int result; | ||
| try (Arena arena = Arena.ofConfined()) { |
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 register method is very performance critical (the equivalents on Linux might be called >1M/sec) so need to see the impact of creating a confined arena and allocating. I assumed it would use BufferStack buf maybe there is a reason it is not used here?
| kevent.flags(keventMS, (short) flags); | ||
| // rest default to zero | ||
|
|
||
| // this do-while replaces restartable |
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.
Future maintainers may ask what "replaces restartable" means, I think better to say that it calls kqueue if the syscall is interrupted (EINTR).
|
@DarraghClarke @minborg Would it be possible to provide a brief summary on what modifications have been done to the jextract-generated classes? The use of FFMUtils jumps out. Ideally they would be checked in without modification as it makes it easy to re-generate. It might be that this never happens but there is something a bit uncomfortable about checking in modified sources. |
|
Thanks for the thorough review, I'll address all these in the next commit. |
I think we could document the changes where we document how jextract is run. Ideally, we spoke about |
That might be okay. In the distant past the in-tree copy of the zlib code needed a few patches. The list of changes, and reasoning, went into a readme file to help future sync ups. It should be very rare that we need to re-run jextract but if we do then it's important to document the patches. |
|
So I double checked the changes made there and wrote this up, I can make it more precise or change wording if its going to be attached to a file. Package info currently contains the script used to generate the code Common changes:
kqueue_h specific changes:
|
It might also help if the integration were split into two. A first commit with the jextract-generated files, then a second that has the changes. That would capture in the git log the changes that were made, and would also make things much easier to review too as it would be obvious what has been changed. |
|
@DarraghClarke This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply issue a |
|
Closed as this ticket is being addressed in 2 other PRs, the first of which #27590 is already integrated, and the second will be opened shortly and will be linked from here also |
This PR aims to Panamize the Java Kqueue implementation, This is based on the work that was previously shared in #22307 , The main change since then is that this branch takes advantage of the changes made in #25043 to allow for better performance during errno handling.
These changes feature a lot of Jextract generated files, though alterations have been made in relation to Errno handling and performance improvements.
I will update this description soon to include performance metrics on a few microbenchmarks, though currently it's roughly 2% to 3% slower with the changes, which is somewhat expected, though there are still a few ideas of possible performance improvements that could be tried. Any suggestions or comments in that area are more than welcome however.
Progress
Issue
Reviewers
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/25546/head:pull/25546$ git checkout pull/25546Update a local copy of the PR:
$ git checkout pull/25546$ git pull https://git.openjdk.org/jdk.git pull/25546/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 25546View PR using the GUI difftool:
$ git pr show -t 25546Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/25546.diff
Using Webrev
Link to Webrev Comment