fix(storage/esp_libc): Fix poll return value discrepancy

This commit is contained in:
Tomáš Rohlínek
2026-01-14 08:28:05 +01:00
parent d66e59eae4
commit d7344e9658
2 changed files with 117 additions and 9 deletions
+17 -8
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -22,7 +22,6 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
fd_set readfds; fd_set readfds;
fd_set writefds; fd_set writefds;
fd_set errorfds; fd_set errorfds;
int ret = 0;
if (fds == NULL) { if (fds == NULL) {
errno = ENOENT; errno = ENOENT;
@@ -43,7 +42,6 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
if (fds[i].fd >= FD_SETSIZE) { if (fds[i].fd >= FD_SETSIZE) {
fds[i].revents |= POLLNVAL; fds[i].revents |= POLLNVAL;
++ret;
continue; continue;
} }
@@ -62,10 +60,17 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
const int select_ret = select(max_fd + 1, &readfds, &writefds, &errorfds, timeout < 0 ? NULL : &tv); const int select_ret = select(max_fd + 1, &readfds, &writefds, &errorfds, timeout < 0 ? NULL : &tv);
if (select_ret > 0) { if (select_ret < 0) {
ret += select_ret; // keeping the errno from select()
return select_ret;
}
if (select_ret > 0) {
for (unsigned int i = 0; i < nfds; ++i) { for (unsigned int i = 0; i < nfds; ++i) {
if (fds[i].fd < 0 || fds[i].fd >= FD_SETSIZE) {
continue;
}
if (FD_ISSET(fds[i].fd, &readfds)) { if (FD_ISSET(fds[i].fd, &readfds)) {
fds[i].revents |= POLLIN; fds[i].revents |= POLLIN;
} }
@@ -78,9 +83,13 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
fds[i].revents |= POLLERR; fds[i].revents |= POLLERR;
} }
} }
} else { }
ret = select_ret;
// keeping the errno from select() int ret = 0;
for (unsigned int i = 0; i < nfds; ++i) {
if (fds[i].revents != 0) {
++ret;
}
} }
return ret; return ret;
+100 -1
View File
@@ -1,14 +1,16 @@
/* /*
* SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/select.h> #include <sys/select.h>
#include <stdbool.h>
#include "unity.h" #include "unity.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "driver/uart.h" #include "driver/uart.h"
@@ -37,6 +39,76 @@ typedef struct {
static const char message[] = "Hello world!"; static const char message[] = "Hello world!";
typedef struct {
fd_set *readfds;
fd_set *writefds;
int local_fd;
bool ready_read;
bool ready_write;
} dummy_select_args_t;
static int dummy_open(const char *path, int flags, int mode)
{
return 0; // single local fd
}
static int dummy_close(int fd)
{
return 0;
}
static esp_err_t dummy_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t sem, void **end_select_args)
{
(void) nfds;
(void) exceptfds;
dummy_select_args_t *args = calloc(1, sizeof(dummy_select_args_t));
if (args == NULL) {
return ESP_ERR_NO_MEM;
}
args->local_fd = 0;
args->readfds = readfds;
args->writefds = writefds;
args->ready_read = readfds && FD_ISSET(args->local_fd, readfds);
args->ready_write = writefds && FD_ISSET(args->local_fd, writefds);
if (!(args->ready_read || args->ready_write)) {
free(args);
*end_select_args = NULL;
return ESP_OK;
}
*end_select_args = args;
esp_vfs_select_triggered(sem);
return ESP_OK;
}
static esp_err_t dummy_end_select(void *end_select_args)
{
dummy_select_args_t *args = end_select_args;
if (!args) {
return ESP_OK;
}
if (args->ready_read && args->readfds) {
FD_SET(args->local_fd, args->readfds);
}
if (args->ready_write && args->writefds) {
FD_SET(args->local_fd, args->writefds);
}
free(args);
return ESP_OK;
}
static const esp_vfs_t dummy_vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.open = dummy_open,
.close = dummy_close,
.start_select = dummy_start_select,
.end_select = dummy_end_select,
};
static int open_dummy_socket(void) static int open_dummy_socket(void)
{ {
const struct addrinfo hints = { const struct addrinfo hints = {
@@ -526,6 +598,33 @@ TEST_CASE("UART can do poll() with POLLIN event", "[vfs]")
deinit(uart_fd, socket_fd); deinit(uart_fd, socket_fd);
} }
TEST_CASE("poll() counts fd once when multiple events are set", "[vfs]")
{
ESP_ERROR_CHECK(esp_vfs_register("/dummy", &dummy_vfs, NULL));
const int fd = open("/dummy/test", O_RDONLY);
TEST_ASSERT_GREATER_OR_EQUAL(0, fd);
struct pollfd poll_fds[] = {
{
.fd = fd,
.events = POLLIN | POLLOUT,
},
{
.fd = -1,
},
};
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(1, s);
TEST_ASSERT_EQUAL(fd, poll_fds[0].fd);
TEST_ASSERT_EQUAL(POLLIN | POLLOUT, poll_fds[0].revents);
TEST_ASSERT_EQUAL(-1, poll_fds[1].fd);
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
close(fd);
ESP_ERROR_CHECK(esp_vfs_unregister("/dummy"));
}
TEST_CASE("UART can do poll() with POLLOUT event", "[vfs]") TEST_CASE("UART can do poll() with POLLOUT event", "[vfs]")
{ {
int uart_fd; int uart_fd;