kernel: Properly report that no events were selected in select_fd.

Adjust wait_for_objects/select/poll implementations to handle this
properly (use the original array's selected-events values, not
the select info's values, and ignore errors if events were reported.)

event_queue is not adjusted and will behave differently after this
change (specifically it will not allow such FDs to be added,
which matches behavior of epoll/kqueue elsewhere.)

Change-Id: Icea26efce894f00697afd29f3bf51b7e60e522ab
This commit is contained in:
Augustin Cavalier
2024-07-16 15:32:59 -04:00
parent 9aaf80afaa
commit 7f86e9ee17
4 changed files with 23 additions and 19 deletions
+2 -4
View File
@@ -1615,9 +1615,8 @@ devfs_select(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
// If the device has no select() hook, notify select() now. // If the device has no select() hook, notify select() now.
if (!vnode->stream.u.dev.device->HasSelect()) { if (!vnode->stream.u.dev.device->HasSelect()) {
if (!SELECT_TYPE_IS_OUTPUT_ONLY(event)) if (!SELECT_TYPE_IS_OUTPUT_ONLY(event))
return notify_select_event((selectsync*)sync, event); notify_select_event((selectsync*)sync, event);
else return B_UNSUPPORTED;
return B_OK;
} }
return vnode->stream.u.dev.device->Select(cookie->device_cookie, event, return vnode->stream.u.dev.device->Select(cookie->device_cookie, event,
@@ -1635,7 +1634,6 @@ devfs_deselect(fs_volume* _volume, fs_vnode* _vnode, void* _cookie,
if (!S_ISCHR(vnode->stream.type)) if (!S_ISCHR(vnode->stream.type))
return B_NOT_ALLOWED; return B_NOT_ALLOWED;
// If the device has no select() hook, notify select() now.
if (!vnode->stream.u.dev.device->HasDeselect()) if (!vnode->stream.u.dev.device->HasDeselect())
return B_OK; return B_OK;
+15 -9
View File
@@ -535,7 +535,7 @@ common_select(int numFDs, fd_set *readSet, fd_set *writeSet, fd_set *errorSet,
fd_zero(errorSet, numFDs); fd_zero(errorSet, numFDs);
if (status == B_OK) { if (status == B_OK) {
for (count = 0, fd = 0;fd < numFDs; fd++) { for (count = 0, fd = 0; fd < numFDs; fd++) {
if (readSet && sync->set[fd].events & (SELECT_FLAG(B_SELECT_READ) if (readSet && sync->set[fd].events & (SELECT_FLAG(B_SELECT_READ)
| SELECT_FLAG(B_SELECT_DISCONNECTED) | SELECT_FLAG(B_SELECT_ERROR))) { | SELECT_FLAG(B_SELECT_DISCONNECTED) | SELECT_FLAG(B_SELECT_ERROR))) {
FD_SET(fd, readSet); FD_SET(fd, readSet);
@@ -584,12 +584,16 @@ common_poll(struct pollfd *fds, nfds_t numFDs, bigtime_t timeout,
int fd = fds[i].fd; int fd = fds[i].fd;
// initialize events masks // initialize events masks
sync->set[i].selected_events = fds[i].events fds[i].events |= POLLNVAL | POLLERR | POLLHUP;
| POLLNVAL | POLLERR | POLLHUP; sync->set[i].selected_events = fds[i].events;
sync->set[i].events = 0; sync->set[i].events = 0;
fds[i].revents = 0; fds[i].revents = 0;
if (fd >= 0 && select_fd(fd, sync->set + i, kernel) != B_OK) { if (fd >= 0 && select_fd(fd, sync->set + i, kernel) != B_OK) {
// If the FD returned events as well as an error, ignore the error.
if (sync->set[i].events != 0)
continue;
sync->set[i].events = POLLNVAL; sync->set[i].events = POLLNVAL;
fds[i].revents = POLLNVAL; fds[i].revents = POLLNVAL;
// indicates that the FD doesn't need to be deselected // indicates that the FD doesn't need to be deselected
@@ -634,8 +638,7 @@ common_poll(struct pollfd *fds, nfds_t numFDs, bigtime_t timeout,
continue; continue;
// POLLxxx flags and B_SELECT_xxx flags are compatible // POLLxxx flags and B_SELECT_xxx flags are compatible
fds[i].revents = sync->set[i].events fds[i].revents = sync->set[i].events & fds[i].events;
& sync->set[i].selected_events;
if (fds[i].revents != 0) if (fds[i].revents != 0)
count++; count++;
} }
@@ -676,12 +679,16 @@ common_wait_for_objects(object_wait_info* infos, int numInfos, uint32 flags,
int32 object = infos[i].object; int32 object = infos[i].object;
// initialize events masks // initialize events masks
sync->set[i].selected_events = infos[i].events infos[i].events |= B_EVENT_INVALID | B_EVENT_ERROR | B_EVENT_DISCONNECTED;
| B_EVENT_INVALID | B_EVENT_ERROR | B_EVENT_DISCONNECTED; sync->set[i].selected_events = infos[i].events;
sync->set[i].events = 0; sync->set[i].events = 0;
infos[i].events = 0; infos[i].events = 0;
if (select_object(type, object, sync->set + i, kernel) != B_OK) { if (select_object(type, object, sync->set + i, kernel) != B_OK) {
// If the object returned events as well as an error, ignore the error.
if (sync->set[i].events != 0)
continue;
sync->set[i].events = B_EVENT_INVALID; sync->set[i].events = B_EVENT_INVALID;
infos[i].events = B_EVENT_INVALID; infos[i].events = B_EVENT_INVALID;
// indicates that the object doesn't need to be deselected // indicates that the object doesn't need to be deselected
@@ -708,8 +715,7 @@ common_wait_for_objects(object_wait_info* infos, int numInfos, uint32 flags,
ssize_t count = 0; ssize_t count = 0;
if (status == B_OK) { if (status == B_OK) {
for (int i = 0; i < numInfos; i++) { for (int i = 0; i < numInfos; i++) {
infos[i].events = sync->set[i].events infos[i].events &= sync->set[i].events;
& sync->set[i].selected_events;
if (infos[i].events != 0) if (infos[i].events != 0)
count++; count++;
} }
+4 -3
View File
@@ -562,9 +562,10 @@ select_fd(int32 fd, struct select_info* info, bool kernel)
// immediately notify the select call // immediately notify the select call
eventsToSelect &= ~SELECT_OUTPUT_ONLY_FLAGS; eventsToSelect &= ~SELECT_OUTPUT_ONLY_FLAGS;
if (eventsToSelect != 0) if (eventsToSelect != 0)
return notify_select_events(info, eventsToSelect); notify_select_events(info, eventsToSelect);
else
return B_OK; info->selected_events = 0;
return B_UNSUPPORTED;
} }
// We need the FD to stay open while we're doing this, so no select()/ // We need the FD to stay open while we're doing this, so no select()/
+2 -3
View File
@@ -5847,9 +5847,8 @@ file_select(struct file_descriptor* descriptor, uint8 event,
// If the FS has no select() hook, notify select() now. // If the FS has no select() hook, notify select() now.
if (!HAS_FS_CALL(vnode, select)) { if (!HAS_FS_CALL(vnode, select)) {
if (!SELECT_TYPE_IS_OUTPUT_ONLY(event)) if (!SELECT_TYPE_IS_OUTPUT_ONLY(event))
return notify_select_event(sync, event); notify_select_event(sync, event);
else return B_UNSUPPORTED;
return B_OK;
} }
return FS_CALL(vnode, select, descriptor->cookie, event, sync); return FS_CALL(vnode, select, descriptor->cookie, event, sync);