VFS: Implement poll() based on select()

Closes https://github.com/espressif/esp-idf/issues/2945
This commit is contained in:
Roland Dobai 2019-02-19 13:18:40 +01:00
parent 5ec58c316d
commit aa9d302814
6 changed files with 294 additions and 12 deletions

View File

@ -3,6 +3,7 @@ set(COMPONENT_SRCS "locks.c"
"random.c"
"reent_init.c"
"select.c"
"poll.c"
"syscall_table.c"
"syscalls.c"
"termios.c"

View File

@ -1,4 +1,4 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -11,22 +11,38 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_PLATFORM_SYS_POLL_H_
#define _ESP_PLATFORM_SYS_POLL_H_
#define POLLIN 0x0001 /* any readable data available */
#define POLLOUT 0x0004 /* file descriptor is writeable */
#define POLLPRI 0x0002 /* OOB/Urgent readable data */
#define POLLERR 0x0008 /* some poll error occurred */
#define POLLHUP 0x0010 /* file descriptor was "hung up" */
#define POLLIN (1u << 0) /* data other than high-priority may be read without blocking */
#define POLLRDNORM (1u << 1) /* normal data may be read without blocking */
#define POLLRDBAND (1u << 2) /* priority data may be read without blocking */
#define POLLPRI (POLLRDBAND) /* high-priority data may be read without blocking */
// Note: POLLPRI is made equivalent to POLLRDBAND in order to fit all these events into one byte
#define POLLOUT (1u << 3) /* normal data may be written without blocking */
#define POLLWRNORM (POLLOUT) /* equivalent to POLLOUT */
#define POLLWRBAND (1u << 4) /* priority data my be written */
#define POLLERR (1u << 5) /* some poll error occurred */
#define POLLHUP (1u << 6) /* file descriptor was "hung up" */
#define POLLNVAL (1u << 7) /* the specified file descriptor is invalid */
#ifdef __cplusplus
extern "C" {
#endif
struct pollfd {
int fd; /* The descriptor. */
short events; /* The event(s) is/are specified here. */
short revents; /* Events found are returned here. */
int fd; /* The descriptor. */
short events; /* The event(s) is/are specified here. */
short revents; /* Events found are returned here. */
};
typedef unsigned int nfds_t;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _ESP_PLATFORM_SYS_POLL_H_

21
components/newlib/poll.c Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/poll.h>
#include "esp_vfs.h"
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
return esp_vfs_poll(fds, nfds, timeout);
}

View File

@ -1,4 +1,4 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -28,6 +28,7 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/termios.h>
#include <sys/poll.h>
#include <dirent.h>
#include <string.h>
#include "sdkconfig.h"
@ -385,6 +386,22 @@ void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem);
*/
void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken);
/**
* @brief Implements the VFS layer for synchronous I/O multiplexing by poll()
*
* The implementation is based on esp_vfs_select. The parameters and return values are compatible with POSIX poll().
*
* @param fds Pointer to the array containing file descriptors and events poll() should consider.
* @param nfds Number of items in the array fds.
* @param timeout Poll() should wait at least timeout milliseconds. If the value is 0 then it should return
* immediately. If the value is -1 then it should wait (block) until the event occurs.
*
* @return A positive return value indicates the number of file descriptors that have been selected. The 0
* return value indicates a timed-out poll. -1 is return on failure and errno is set accordingly.
*
*/
int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -1,4 +1,4 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -195,6 +195,67 @@ TEST_CASE("UART can do select()", "[vfs]")
deinit(uart_fd, socket_fd);
}
TEST_CASE("UART can do poll()", "[vfs]")
{
int uart_fd;
int socket_fd;
char recv_message[sizeof(message)];
init(&uart_fd, &socket_fd);
struct pollfd poll_fds[] = {
{
.fd = uart_fd,
.events = POLLIN,
},
{
.fd = -1, // should be ignored according to the documentation of poll()
},
};
const test_task_param_t test_task_param = {
.fd = uart_fd,
.delay_ms = 50,
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(test_task_param.sem);
start_task(&test_task_param);
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
TEST_ASSERT_EQUAL(POLLIN, poll_fds[0].revents);
TEST_ASSERT_EQUAL(-1, poll_fds[1].fd);
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
int read_bytes = read(uart_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
poll_fds[1].fd = socket_fd;
poll_fds[1].events = POLLIN;
start_task(&test_task_param);
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
TEST_ASSERT_EQUAL(POLLIN, poll_fds[0].revents);
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
read_bytes = read(uart_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
vSemaphoreDelete(test_task_param.sem);
deinit(uart_fd, socket_fd);
}
TEST_CASE("socket can do select()", "[vfs]")
{
int uart_fd;
@ -239,6 +300,58 @@ TEST_CASE("socket can do select()", "[vfs]")
close(dummy_socket_fd);
}
TEST_CASE("socket can do poll()", "[vfs]")
{
int uart_fd;
int socket_fd;
char recv_message[sizeof(message)];
init(&uart_fd, &socket_fd);
const int dummy_socket_fd = open_dummy_socket();
struct pollfd poll_fds[] = {
{
.fd = uart_fd,
.events = POLLIN,
},
{
.fd = socket_fd,
.events = POLLIN,
},
{
.fd = dummy_socket_fd,
.events = POLLIN,
},
};
const test_task_param_t test_task_param = {
.fd = socket_fd,
.delay_ms = 50,
.sem = xSemaphoreCreateBinary(),
};
TEST_ASSERT_NOT_NULL(test_task_param.sem);
start_task(&test_task_param);
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(s, 1);
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
TEST_ASSERT_EQUAL(POLLIN, poll_fds[1].revents);
TEST_ASSERT_EQUAL(dummy_socket_fd, poll_fds[2].fd);
TEST_ASSERT_EQUAL(0, poll_fds[2].revents);
int read_bytes = read(socket_fd, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
vSemaphoreDelete(test_task_param.sem);
deinit(uart_fd, socket_fd);
close(dummy_socket_fd);
}
TEST_CASE("select() timeout", "[vfs]")
{
int uart_fd;
@ -270,6 +383,44 @@ TEST_CASE("select() timeout", "[vfs]")
deinit(uart_fd, socket_fd);
}
TEST_CASE("poll() timeout", "[vfs]")
{
int uart_fd;
int socket_fd;
init(&uart_fd, &socket_fd);
struct pollfd poll_fds[] = {
{
.fd = uart_fd,
.events = POLLIN,
},
{
.fd = socket_fd,
.events = POLLIN,
},
};
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(s, 0);
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
poll_fds[0].fd = -1;
poll_fds[1].fd = -1;
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
TEST_ASSERT_EQUAL(s, 0);
TEST_ASSERT_EQUAL(-1, poll_fds[0].fd);
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
TEST_ASSERT_EQUAL(-1, poll_fds[1].fd);
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
deinit(uart_fd, socket_fd);
}
static void select_task(void *param)
{
const test_task_param_t *test_task_param = param;

View File

@ -1,4 +1,4 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -1110,3 +1110,79 @@ int esp_vfs_utime(const char *path, const struct utimbuf *times)
CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times);
return ret;
}
int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
struct timeval tv = {
// timeout is in milliseconds
.tv_sec = timeout / 1000,
.tv_usec = (timeout % 1000) * 1000,
};
int max_fd = -1;
fd_set readfds;
fd_set writefds;
fd_set errorfds;
struct _reent* r = __getreent();
int ret = 0;
if (fds == NULL) {
__errno_r(r) = ENOENT;
return -1;
}
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&errorfds);
for (int i = 0; i < nfds; ++i) {
fds[i].revents = 0;
if (fds[i].fd < 0) {
// revents should remain 0 and events ignored (according to the documentation of poll()).
continue;
}
if (fds[i].fd >= MAX_FDS) {
fds[i].revents |= POLLNVAL;
++ret;
continue;
}
if (fds[i].events & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
FD_SET(fds[i].fd, &readfds);
FD_SET(fds[i].fd, &errorfds);
max_fd = MAX(max_fd, fds[i].fd);
}
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET(fds[i].fd, &writefds);
FD_SET(fds[i].fd, &errorfds);
max_fd = MAX(max_fd, fds[i].fd);
}
}
const int select_ret = esp_vfs_select(max_fd + 1, &readfds, &writefds, &errorfds, timeout < 0 ? NULL: &tv);
if (select_ret > 0) {
ret += select_ret;
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(fds[i].fd, &readfds)) {
fds[i].revents |= POLLIN;
}
if (FD_ISSET(fds[i].fd, &writefds)) {
fds[i].revents |= POLLOUT;
}
if (FD_ISSET(fds[i].fd, &errorfds)) {
fds[i].revents |= POLLERR;
}
}
} else {
ret = select_ret;
// keeping the errno from select()
}
return ret;
}