Repository navigation
Conversation
2dfc369 to
ca734ca
Compare
|
Windows test: head AI-assisted (Claude Code). |
|
Thanks for the Windows run — that is the one platform I cannot check here, so it closes half of the checklist. The other half, which your note says you did not run: the cases this PR adds fail on Coordination with #4924 and #4933The overlap note points at the same lines of Once #4924 or #4933 lands, the native lookup they register with libkernel is the natural source for that answer, and I am happy to rebase this on whichever lands first and ask it there instead of deriving the kind from the host descriptor alone. |
… events Before: NOTE_LOWAT and a size above one in sceKernelAddReadEvent and sceKernelAddWriteEvent threw, so a title waiting for a number of bytes could not register the event at all (PS5MC and XPSemu import both the kevent filter and the two calls). Now the knote holds the event until the socket has the requested bytes, or the requested free space, as FreeBSD's filt_soread and filt_sowrite do before they compare kn_data against the mark; an end of file is still reported below it. A kevent that re-adds the knote without NOTE_LOWAT clears the watermark, as kqueue_register copies the whole kevent over it. A mark is not something poll readiness can express: a socket below the mark is already readable, so a wait that has one returns to its caller every millisecond instead of spinning on the readable socket, the slice the pipe path already uses.
Before: a read or write event on a descriptor that is not a guest socket threw, so a title that registers one on a pipe could not do it at all, and the event a file answers there was a throw too. Now the kind of the descriptor decides, as FreeBSD's filter sets do: a socket and a pipe carry the read and write filters, a file and a directory only carry the vnode one and answer EINVAL as filt_fileattach does, and a descriptor that is none of those throws, because the terminal filters of tty_kqops_read_event and tty_kqops_write_event are not modelled. The filters follow filt_piperead and filt_pipewrite: a read event reports the bytes in the pipe buffer and returns once there is any, a write event reports the free space the host exposes and returns once there is any, an end of file is set when the other end is closed, and the low water mark of a kevent change or of sceKernelAddReadEvent and sceKernelAddWriteEvent holds the event below it as it does for a socket. A socket is recognised by socket-table membership rather than by a descriptor number, since those are ordinary ones, and the wait adds a pipe descriptor to the set it polls, without which a blocked kevent would never see the event. A pipe is not pollable on Windows, so there the wait slices at a millisecond.
A descriptor that is neither a socket, a pipe nor a file still threw a read or write event, which left sceKernelAddReadEvent and sceKernelAddWriteEvent with an unimplemented path. A terminal is what was missing: ttydev_kqfilter attaches the read and write filters to it, so tty_kqops_read_event and tty_kqops_write_event apply, with the number ttydisc_read_poll reports, the readable lines a canonical terminal has, and that of ttydisc_write_poll, the space left in the output queue, which the host does not expose, so a writable terminal reports the lower bound of one byte. A character device that is not a terminal, and a file or a directory, answer EINVAL, as kqueue_register does for a filter the descriptor has none of. The kind a knote was attached to is kept in it, the way f_attach fixes the filter at registration: a terminal revoked later still reports its end of file through the terminal filter instead of being read as a device with no filter, which is what asking the host again would report.
ca734ca to
6f51e1d
Compare
|
Rebased on current Four files conflicted ( One thing the rebuild caught, worth mentioning because it is the kind of thing a rebase hides: Local verification on |
CnRJay
left a comment
There was a problem hiding this comment.
The filter selection by descriptor kind follows FreeBSD's filter sets and looks right. One Windows timing point this extends to kevent: a pipe in the set makes GuestSockets::WaitAny loop with a 1 ms slice (WSAPoll(..., slice) when sockets are present, std::this_thread::sleep_for(1ms) otherwise). Both follow the coarse Windows timer, so each slice usually takes about 15.6 ms rather than 1 ms (#1471). That's why #4980 and #4754 move the poll and select sleeps to PreciseSleepUs.
For kevent that matters more than for poll. A thread parked in kevent on a pipe (an event loop's wake-up pipe, a log pipe) now sees each write up to ~15 ms late. With NOTE_LOWAT from #4908 on top, the wait rechecks at the same coarse rate. Using PreciseSleepUs(1000) for the pipe-only slice, and a high-resolution wait for the mixed case (for example a pipe-ready event, or splitting the socket wait into a 0-timeout WSAPoll plus PreciseSleepUs), would make "every millisecond" actually 1 ms. Since it's the shared loop, it could also be a separate small PR that both this and #4908 depend on.
|
Closing: no title is known to reach this. With the queue at 480+ open PRs, please hold off on new PRs that don't name a title needing the change until it's down. If a title stops on this on current main, reopen with the title ID and the log line. |
What
A read or write event on a descriptor that is not a guest socket threw, in
keventand insceKernelAddReadEvent/sceKernelAddWriteEventalike, so a title that registers one on a pipe or on the console could not do it at all.The kind of the descriptor now decides, following FreeBSD 11's filter sets: a socket, a pipe and a terminal carry the read and write filters (
filt_soread,filt_sowrite,filt_piperead,filt_pipewrite, andtty_kqops_read_event/tty_kqops_write_eventbehindttydev_kqfilter), a file and a directory only carry the vnode filter and answerEINVALasfilt_fileattachdoes, and so does a character device that is not a terminal, askqueue_registeranswersEINVALfor a filter the descriptor has none of. The kind isGuestSockets::Kind, which asks the socket table first and then the host descriptor type — never a number range, since socket descriptors are ordinary ones — and it is kept in the knote, the wayf_attachfixes the filter at registration, so a terminal revoked later still reports its end of file through the terminal filter.The pipe filters report what
filt_pipereadandfilt_pipewritedo: a read event carries the bytes in the pipe buffer and returns once there is any, a write event carries the free space the host exposes and returns once there is any, and an end of file is set when the other end is closed. A terminal reports the bytes of a readable canonical line, which is whatttydisc_read_pollcounts throughttyinq_bytescanonicalized, and the space left in its output queue, whichttydisc_write_pollcounts throughttyoutq_bytesleftand the host does not expose, so a writable terminal carries the lower bound of one byte. The low water mark (NOTE_LOWAT, or thesizeof the two calls) holds any of them below the mark.Two things were needed for a wait to see a descriptor that is not a socket: the mark path and the wait now share one readiness helper, and
GuestSockets::WaitAnyadds a pipe descriptor to the set it polls, without which a thread blocked inkeventwould never wake on it. Windows pipes are not sockets andWSAPollcannot take them, so there the wait returns every millisecond instead.Tested
Linux (Fedora 44, GCC 16.2.1), full build,
ctest --test-dir build -E "agc_|video_out|lds_|msaa"534/534,python3 tools/check_conventions.py --base origin/mainclean.New cases:
GuestKqueue.cppregisters a read and a write event on a pipe, checks that an empty pipe is not readable, that the write event reports free space, that a read event reports the bytes in the buffer, that aNOTE_LOWATmark holds it below the mark and that the last writer closing setsEV_EOF; it does the same for a terminal over a host pty, where a partial line is not readable, a complete one reports its bytes, the terminal going away setsEV_EOF, and a device with no filters of its own answersEINVAL; a file, a directory and a closed descriptor answerEINVALandEBADF, replacing the assertions that a pipe threw.GuestSocketEvents.cppdoes the same throughsceKernelAddReadEvent'ssizeon a pipe and on a terminal, with the wait blocked on its own equeue and a thread writing, so a wait that does not see them fails instead of passing silently.Not tested on the console, and not reached at run time yet: PS5MC (PPSA99420) and XPSemu (PPSA97358) import
keventand both calls, andtools/import_audit.pyon their relinked registries reports them as stubs, but each stops earlier on an absent import (ixrw0h2tWuI,chflags, andx7g7Ebeo8-U,mlockall) that this PR does not touch. The semantics follow the FreeBSD 11 sources the existing filters were written from (kern_event.c,uipc_socket.c,tty.c,sys/sys/ttyqueue.h).Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)keventand these calls; neither reaches them yetNote on coordination: #4391 gives guest sockets ordinary descriptors and rewrites the socket checks of this file to socket-table membership. This PR takes the membership check with it (in
DescriptorFilterErrorand by asking the socket table before the host descriptor type), so the two do not have to be merged in a particular order.