kqueue: Implement EV_RECEIPT.

Tested with a modified FreeBSD test.

Also fix EVFILT_READ + EVFILT_WRITE failing to select only returning
one error, and not two as it's supposed to.
This commit is contained in:
Augustin Cavalier
2026-07-09 15:36:05 -04:00
parent 1699186567
commit 53630a31a0
2 changed files with 66 additions and 20 deletions
+1
View File
@@ -91,6 +91,7 @@ struct kevent {
/* flags */ /* flags */
#define EV_ONESHOT 0x0010 /* only report one occurrence */ #define EV_ONESHOT 0x0010 /* only report one occurrence */
#define EV_CLEAR 0x0020 /* clear event state after reporting */ #define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
/* returned values */ /* returned values */
#define EV_EOF 0x8000 /* EOF detected */ #define EV_EOF 0x8000 /* EOF detected */
+51 -6
View File
@@ -48,9 +48,11 @@ kevent(int kq,
const struct timespec *tspec) const struct timespec *tspec)
{ {
BStackOrHeapArray<event_wait_info, 16> waitInfos(max_c(nchanges, nevents)); BStackOrHeapArray<event_wait_info, 16> waitInfos(max_c(nchanges, nevents));
BStackOrHeapArray<int32, 16> requestedEvents(nchanges);
event_wait_info* waitInfo = waitInfos; event_wait_info* waitInfo = waitInfos;
int changedInfos = 0; int32* requestedEvent = requestedEvents;
int changedInfos = 0, receiptCount = 0;
for (int i = 0; i < nchanges; i++) { for (int i = 0; i < nchanges; i++) {
waitInfo->object = changelist[i].ident; waitInfo->object = changelist[i].ident;
@@ -147,30 +149,72 @@ kevent(int kq,
if (waitInfo->events != 0) if (waitInfo->events != 0)
waitInfo->events |= behavior; waitInfo->events |= behavior;
*requestedEvent = waitInfo->events;
if ((changelist[i].flags & EV_RECEIPT) != 0) {
receiptCount++;
// Use sign bit to indicate EV_RECEIPT.
*requestedEvent |= (1 << 31);
}
changedInfos++; changedInfos++;
waitInfo++; waitInfo++;
requestedEvent++;
if (receiptCount >= nevents)
break;
} }
if (changedInfos != 0) { if (changedInfos != 0) {
status_t status = _kern_event_queue_select(kq, waitInfos, changedInfos); status_t status = _kern_event_queue_select(kq, waitInfos, changedInfos);
if (status != B_OK) { if (status != B_OK && nchanges == 1 && nevents == 0) {
if (nchanges == 1 && nevents == 0) {
// Special case: return the lone error directly. // Special case: return the lone error directly.
__set_errno(waitInfos[0].events); __set_errno(waitInfos[0].events);
return -1; return -1;
} }
// Report problems as error events. // Report problems (or successes, if EV_RECEIPT is set) as error events.
int errors = 0; int errors = 0;
for (int i = 0; i < changedInfos; i++) { for (int i = 0; i < changedInfos; i++) {
if (waitInfos[i].events > 0) int64_t data = waitInfos[i].events;
if (data > 0) {
if (requestedEvents[i] > 0)
continue; continue;
// Always generate an "error" event for EV_RECEIPT.
data = 0;
}
if (nevents == 0) { if (nevents == 0) {
errors = -1; errors = -1;
break; break;
} }
short filter = filter_from_info(waitInfos[i]); short filter = filter_from_info(waitInfos[i]);
int64_t data = waitInfos[i].events; if ((requestedEvents[i] & (B_EVENT_READ | B_EVENT_WRITE))
== (B_EVENT_READ | B_EVENT_WRITE)) {
// We need to generate two errors for this case.
filter = EVFILT_READ;
int64_t readData = data;
if (data == 0 && (waitInfos[i].events & B_EVENT_READ) == 0)
readData = -1;
EV_SET(eventlist, waitInfos[i].object,
filter, EV_ERROR, 0, readData, waitInfos[i].user_data);
eventlist++;
nevents--;
errors++;
filter = EVFILT_WRITE;
if (data == 0 && (waitInfos[i].events & B_EVENT_WRITE) == 0)
data = -1;
}
if (nevents == 0) {
errors = -1;
break;
}
EV_SET(eventlist, waitInfos[i].object, EV_SET(eventlist, waitInfos[i].object,
filter, EV_ERROR, 0, data, waitInfos[i].user_data); filter, EV_ERROR, 0, data, waitInfos[i].user_data);
eventlist++; eventlist++;
@@ -180,6 +224,7 @@ kevent(int kq,
if (errors > 0) if (errors > 0)
return errors; return errors;
if (status != B_OK) {
__set_errno(status); __set_errno(status);
return -1; return -1;
} }