From 7e9d90b180fb6805acfb3ebaeb2db34230674805 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Wed, 10 Jul 2019 14:08:21 +0200 Subject: [PATCH 1/2] VFS: Support concurrent VFS select calls Closes https://github.com/espressif/esp-idf/issues/3392 --- components/vfs/include/esp_vfs.h | 4 +- components/vfs/test/test_vfs_select.c | 148 ++++++++++++----- components/vfs/vfs.c | 25 ++- components/vfs/vfs_uart.c | 231 ++++++++++++++------------ 4 files changed, 253 insertions(+), 155 deletions(-) diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 4d9396c1c..1ab1cb7f6 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -236,7 +236,7 @@ typedef struct #endif // CONFIG_VFS_SUPPORT_TERMIOS /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */ - esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t sem); + esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t sem, void **end_select_args); /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */ int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */ @@ -246,7 +246,7 @@ typedef struct /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */ void* (*get_socket_select_semaphore)(); /** get_socket_select_semaphore returns semaphore allocated in the socket driver; set only for the socket driver */ - void (*end_select)(); + esp_err_t (*end_select)(void *end_select_args); } esp_vfs_t; diff --git a/components/vfs/test/test_vfs_select.c b/components/vfs/test/test_vfs_select.c index c8ff500da..27327d1e5 100644 --- a/components/vfs/test/test_vfs_select.c +++ b/components/vfs/test/test_vfs_select.c @@ -31,6 +31,16 @@ typedef struct { xSemaphoreHandle sem; } test_task_param_t; +typedef struct { + fd_set *rdfds; + fd_set *wrfds; + fd_set *errfds; + int maxfds; + struct timeval *tv; + int select_ret; + xSemaphoreHandle sem; +} test_select_task_param_t; + static const char message[] = "Hello world!"; static int open_dummy_socket() @@ -420,73 +430,121 @@ TEST_CASE("poll() timeout", "[vfs]") deinit(uart_fd, socket_fd); } -static void select_task(void *param) +static void select_task(void *task_param) { - const test_task_param_t *test_task_param = param; - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 100000, - }; + const test_select_task_param_t *param = task_param; - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(test_task_param->fd, &rfds); + int s = select(param->maxfds, param->rdfds, param->wrfds, param->errfds, param->tv); + TEST_ASSERT_EQUAL(param->select_ret, s); - int s = select(test_task_param->fd + 1, &rfds, NULL, NULL, &tv); - TEST_ASSERT_EQUAL(0, s); //timeout - - if (test_task_param->sem) { - xSemaphoreGive(test_task_param->sem); + if (param->sem) { + xSemaphoreGive(param->sem); } vTaskDelete(NULL); } -TEST_CASE("concurent selects work", "[vfs]") +static void inline start_select_task(test_select_task_param_t *param) { - struct timeval tv = { - .tv_sec = 0, - .tv_usec = 100000,//irrelevant - }; + xTaskCreate(select_task, "select_task", 4*1024, (void *) param, 5, NULL); +} +TEST_CASE("concurrent selects work", "[vfs]") +{ int uart_fd, socket_fd; init(&uart_fd, &socket_fd); - const int dummy_socket_fd = open_dummy_socket(); - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(uart_fd, &rfds); + { + // Two tasks will wait for the same UART FD for reading and they will time-out - test_task_param_t test_task_param = { - .fd = uart_fd, - .sem = xSemaphoreCreateBinary(), - }; - TEST_ASSERT_NOT_NULL(test_task_param.sem); + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 100000, + }; - xTaskCreate(select_task, "select_task", 4*1024, (void *) &test_task_param, 5, NULL); - vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select() + fd_set rdfds1; + FD_ZERO(&rdfds1); + FD_SET(uart_fd, &rdfds1); - int s = select(uart_fd + 1, &rfds, NULL, NULL, &tv); - TEST_ASSERT_EQUAL(-1, s); //this select should fail because two selects are accessing UART - //(the other one is waiting for the timeout) - TEST_ASSERT_EQUAL(EINTR, errno); + test_select_task_param_t param = { + .rdfds = &rdfds1, + .wrfds = NULL, + .errfds = NULL, + .maxfds = uart_fd + 1, + .tv = &tv, + .select_ret = 0, // expected timeout + .sem = xSemaphoreCreateBinary(), + }; + TEST_ASSERT_NOT_NULL(param.sem); - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS)); + fd_set rdfds2; + FD_ZERO(&rdfds2); + FD_SET(uart_fd, &rdfds2); + FD_SET(socket_fd, &rdfds2); + FD_SET(dummy_socket_fd, &rdfds2); - FD_ZERO(&rfds); - FD_SET(socket_fd, &rfds); + start_select_task(¶m); + vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select() - test_task_param.fd = dummy_socket_fd; + int s = select(MAX(MAX(uart_fd, dummy_socket_fd), socket_fd) + 1, &rdfds2, NULL, NULL, &tv); + TEST_ASSERT_EQUAL(0, s); // timeout here as well - xTaskCreate(select_task, "select_task", 4*1024, (void *) &test_task_param, 5, NULL); - vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select() + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(param.sem, 1000 / portTICK_PERIOD_MS)); + vSemaphoreDelete(param.sem); + } - s = select(socket_fd + 1, &rfds, NULL, NULL, &tv); - TEST_ASSERT_EQUAL(0, s); //this select should timeout as well as the concurrent one because - //concurrent socket select should work + { + // One tasks waits for UART reading and one for writing. The former will be successful and latter will + // time-out. - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS)); - vSemaphoreDelete(test_task_param.sem); + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 100000, + }; + + fd_set wrfds1; + FD_ZERO(&wrfds1); + FD_SET(uart_fd, &wrfds1); + + test_select_task_param_t param = { + .rdfds = NULL, + .wrfds = &wrfds1, + .errfds = NULL, + .maxfds = uart_fd + 1, + .tv = &tv, + .select_ret = 0, // expected timeout + .sem = xSemaphoreCreateBinary(), + }; + TEST_ASSERT_NOT_NULL(param.sem); + + start_select_task(¶m); + + fd_set rdfds2; + FD_ZERO(&rdfds2); + FD_SET(uart_fd, &rdfds2); + FD_SET(socket_fd, &rdfds2); + FD_SET(dummy_socket_fd, &rdfds2); + + const test_task_param_t send_param = { + .fd = uart_fd, + .delay_ms = 50, + .sem = xSemaphoreCreateBinary(), + }; + TEST_ASSERT_NOT_NULL(send_param.sem); + start_task(&send_param); // This task will write to UART which will be detected by select() + + int s = select(MAX(MAX(uart_fd, dummy_socket_fd), socket_fd) + 1, &rdfds2, NULL, NULL, &tv); + TEST_ASSERT_EQUAL(1, s); + TEST_ASSERT(FD_ISSET(uart_fd, &rdfds2)); + TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rdfds2)); + TEST_ASSERT_UNLESS(FD_ISSET(dummy_socket_fd, &rdfds2)); + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(param.sem, 1000 / portTICK_PERIOD_MS)); + vSemaphoreDelete(param.sem); + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(send_param.sem, 1000 / portTICK_PERIOD_MS)); + vSemaphoreDelete(send_param.sem); + } deinit(uart_fd, socket_fd); close(dummy_socket_fd); diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 0fc5aead3..760d32728 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -794,13 +794,16 @@ int truncate(const char *path, off_t length) return ret; } -static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple) +static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple, void **driver_args) { for (int i = 0; i < end_index; ++i) { const vfs_entry_t *vfs = get_vfs_for_index(i); const fds_triple_t *item = &vfs_fds_triple[i]; if (vfs && vfs->vfs.end_select && item->isset) { - vfs->vfs.end_select(); + esp_err_t err = vfs->vfs.end_select(driver_args[i]); + if (err != ESP_OK) { + ESP_LOGD(TAG, "end_select failed: %s", esp_err_to_name(err)); + } } } } @@ -947,6 +950,15 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds } } + void **driver_args = calloc(s_vfs_count, sizeof(void *)); + + if (driver_args == NULL) { + free(vfs_fds_triple); + __errno_r(r) = ENOMEM; + ESP_LOGD(TAG, "calloc is unsuccessful for driver args"); + return -1; + } + for (int i = 0; i < s_vfs_count; ++i) { const vfs_entry_t *vfs = get_vfs_for_index(i); fds_triple_t *item = &vfs_fds_triple[i]; @@ -958,16 +970,18 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds esp_vfs_log_fd_set("readfds", &item->readfds); esp_vfs_log_fd_set("writefds", &item->writefds); esp_vfs_log_fd_set("errorfds", &item->errorfds); - esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, sel_sem); + esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, sel_sem, + driver_args + i); if (err != ESP_OK) { - call_end_selects(i, vfs_fds_triple); + call_end_selects(i, vfs_fds_triple, driver_args); (void) set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds); if (sel_sem.is_sem_local && sel_sem.sem) { vSemaphoreDelete(sel_sem.sem); sel_sem.sem = NULL; } free(vfs_fds_triple); + free(driver_args); __errno_r(r) = EINTR; ESP_LOGD(TAG, "start_select failed: %s", esp_err_to_name(err)); return -1; @@ -1006,7 +1020,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds xSemaphoreTake(sel_sem.sem, ticks_to_wait); } - call_end_selects(s_vfs_count, vfs_fds_triple); // for VFSs for start_select was called before + call_end_selects(s_vfs_count, vfs_fds_triple, driver_args); // for VFSs for start_select was called before if (ret >= 0) { ret += set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds); } @@ -1015,6 +1029,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds sel_sem.sem = NULL; } free(vfs_fds_triple); + free(driver_args); ESP_LOGD(TAG, "esp_vfs_select returns %d", ret); esp_vfs_log_fd_set("readfds", readfds); diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index 5cb179a4a..7a1d11352 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -111,20 +111,21 @@ static vfs_uart_context_t* s_ctx[UART_NUM] = { #endif }; -/* Lock ensuring that uart_select is used from only one task at the time */ -static _lock_t s_one_select_lock; +typedef struct { + esp_vfs_select_sem_t select_sem; + fd_set *readfds; + fd_set *writefds; + fd_set *errorfds; + fd_set readfds_orig; + fd_set writefds_orig; + fd_set errorfds_orig; +} uart_select_args_t; -static esp_vfs_select_sem_t _select_sem = {.sem = NULL}; -static fd_set *_readfds = NULL; -static fd_set *_writefds = NULL; -static fd_set *_errorfds = NULL; -static fd_set *_readfds_orig = NULL; -static fd_set *_writefds_orig = NULL; -static fd_set *_errorfds_orig = NULL; - - -static void uart_end_select(); +static uart_select_args_t **s_registered_selects = NULL; +static int s_registered_select_num = 0; +static portMUX_TYPE s_registered_select_lock = portMUX_INITIALIZER_UNLOCKED; +static esp_err_t uart_end_select(void *end_select_args); static int uart_open(const char * path, int flags, int mode) { @@ -335,132 +336,156 @@ static int uart_fsync(int fd) return 0; } -static void select_notif_callback(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken) +static esp_err_t register_select(uart_select_args_t *args) { - switch (uart_select_notif) { - case UART_SELECT_READ_NOTIF: - if (FD_ISSET(uart_num, _readfds_orig)) { - FD_SET(uart_num, _readfds); - esp_vfs_select_triggered_isr(_select_sem, task_woken); - } - break; - case UART_SELECT_WRITE_NOTIF: - if (FD_ISSET(uart_num, _writefds_orig)) { - FD_SET(uart_num, _writefds); - esp_vfs_select_triggered_isr(_select_sem, task_woken); - } - break; - case UART_SELECT_ERROR_NOTIF: - if (FD_ISSET(uart_num, _errorfds_orig)) { - FD_SET(uart_num, _errorfds); - esp_vfs_select_triggered_isr(_select_sem, task_woken); - } - break; + esp_err_t ret = ESP_ERR_INVALID_ARG; + + if (args) { + portENTER_CRITICAL(&s_registered_select_lock); + const int new_size = s_registered_select_num + 1; + if ((s_registered_selects = realloc(s_registered_selects, new_size * sizeof(uart_select_args_t *))) == NULL) { + ret = ESP_ERR_NO_MEM; + } else { + s_registered_selects[s_registered_select_num] = args; + s_registered_select_num = new_size; + ret = ESP_OK; + } + portEXIT_CRITICAL(&s_registered_select_lock); } + + return ret; } -static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t select_sem) +static esp_err_t unregister_select(uart_select_args_t *args) { - if (_lock_try_acquire(&s_one_select_lock)) { - return ESP_ERR_INVALID_STATE; + esp_err_t ret = ESP_OK; + if (args) { + ret = ESP_ERR_INVALID_STATE; + portENTER_CRITICAL(&s_registered_select_lock); + for (int i = 0; i < s_registered_select_num; ++i) { + if (s_registered_selects[i] == args) { + const int new_size = s_registered_select_num - 1; + // The item is removed by overwriting it with the last item. The subsequent rellocation will drop the + // last item. + s_registered_selects[i] = s_registered_selects[new_size]; + s_registered_selects = realloc(s_registered_selects, new_size * sizeof(uart_select_args_t *)); + if (s_registered_selects || new_size == 0) { + s_registered_select_num = new_size; + ret = ESP_OK; + } else { + ret = ESP_ERR_NO_MEM; + } + break; + } + } + portEXIT_CRITICAL(&s_registered_select_lock); } + return ret; +} - const int max_fds = MIN(nfds, UART_NUM); - - portENTER_CRITICAL(uart_get_selectlock()); - - if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig || _select_sem.sem) { - portEXIT_CRITICAL(uart_get_selectlock()); - uart_end_select(); - return ESP_ERR_INVALID_STATE; - } - - if ((_readfds_orig = malloc(sizeof(fd_set))) == NULL) { - portEXIT_CRITICAL(uart_get_selectlock()); - uart_end_select(); - return ESP_ERR_NO_MEM; - } - - if ((_writefds_orig = malloc(sizeof(fd_set))) == NULL) { - portEXIT_CRITICAL(uart_get_selectlock()); - uart_end_select(); - return ESP_ERR_NO_MEM; - } - - if ((_errorfds_orig = malloc(sizeof(fd_set))) == NULL) { - portEXIT_CRITICAL(uart_get_selectlock()); - uart_end_select(); - return ESP_ERR_NO_MEM; - } - - //uart_set_select_notif_callback set the callbacks in UART ISR - for (int i = 0; i < max_fds; ++i) { - if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) { - uart_set_select_notif_callback(i, select_notif_callback); +static void select_notif_callback_isr(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken) +{ + portENTER_CRITICAL_ISR(&s_registered_select_lock); + for (int i = 0; i < s_registered_select_num; ++i) { + uart_select_args_t *args = s_registered_selects[i]; + if (args) { + switch (uart_select_notif) { + case UART_SELECT_READ_NOTIF: + if (FD_ISSET(uart_num, &args->readfds_orig)) { + FD_SET(uart_num, args->readfds); + esp_vfs_select_triggered_isr(args->select_sem, task_woken); + } + break; + case UART_SELECT_WRITE_NOTIF: + if (FD_ISSET(uart_num, &args->writefds_orig)) { + FD_SET(uart_num, args->writefds); + esp_vfs_select_triggered_isr(args->select_sem, task_woken); + } + break; + case UART_SELECT_ERROR_NOTIF: + if (FD_ISSET(uart_num, &args->errorfds_orig)) { + FD_SET(uart_num, args->errorfds); + esp_vfs_select_triggered_isr(args->select_sem, task_woken); + } + break; + } } } + portEXIT_CRITICAL_ISR(&s_registered_select_lock); +} - _select_sem = select_sem; +static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + esp_vfs_select_sem_t select_sem, void **end_select_args) +{ + const int max_fds = MIN(nfds, UART_NUM); + *end_select_args = NULL; - _readfds = readfds; - _writefds = writefds; - _errorfds = exceptfds; + uart_select_args_t *args = malloc(sizeof(uart_select_args_t)); - *_readfds_orig = *readfds; - *_writefds_orig = *writefds; - *_errorfds_orig = *exceptfds; + if (args == NULL) { + return ESP_ERR_NO_MEM; + } + args->select_sem = select_sem; + args->readfds = readfds; + args->writefds = writefds; + args->errorfds = exceptfds; + args->readfds_orig = *readfds; // store the original values because they will be set to zero + args->writefds_orig = *writefds; + args->errorfds_orig = *exceptfds; FD_ZERO(readfds); FD_ZERO(writefds); FD_ZERO(exceptfds); + portENTER_CRITICAL(uart_get_selectlock()); + + //uart_set_select_notif_callback sets the callbacks in UART ISR for (int i = 0; i < max_fds; ++i) { - if (FD_ISSET(i, _readfds_orig)) { + if (FD_ISSET(i, &args->readfds_orig) || FD_ISSET(i, &args->writefds_orig) || FD_ISSET(i, &args->errorfds_orig)) { + uart_set_select_notif_callback(i, select_notif_callback_isr); + } + } + + for (int i = 0; i < max_fds; ++i) { + if (FD_ISSET(i, &args->readfds_orig)) { size_t buffered_size; if (uart_get_buffered_data_len(i, &buffered_size) == ESP_OK && buffered_size > 0) { // signalize immediately when data is buffered - FD_SET(i, _readfds); - esp_vfs_select_triggered(_select_sem); + FD_SET(i, readfds); + esp_vfs_select_triggered(args->select_sem); } } } - portEXIT_CRITICAL(uart_get_selectlock()); - // s_one_select_lock is not released on successfull exit - will be - // released in uart_end_select() + esp_err_t ret = register_select(args); + if (ret != ESP_OK) { + portEXIT_CRITICAL(uart_get_selectlock()); + free(args); + return ret; + } + portEXIT_CRITICAL(uart_get_selectlock()); + + *end_select_args = args; return ESP_OK; } -static void uart_end_select() +static esp_err_t uart_end_select(void *end_select_args) { + uart_select_args_t *args = end_select_args; + + if (args) { + free(args); + } + portENTER_CRITICAL(uart_get_selectlock()); + esp_err_t ret = unregister_select(args); for (int i = 0; i < UART_NUM; ++i) { uart_set_select_notif_callback(i, NULL); } - - _select_sem.sem = NULL; - - _readfds = NULL; - _writefds = NULL; - _errorfds = NULL; - - if (_readfds_orig) { - free(_readfds_orig); - _readfds_orig = NULL; - } - - if (_writefds_orig) { - free(_writefds_orig); - _writefds_orig = NULL; - } - - if (_errorfds_orig) { - free(_errorfds_orig); - _errorfds_orig = NULL; - } portEXIT_CRITICAL(uart_get_selectlock()); - _lock_release(&s_one_select_lock); + + return ret; } #ifdef CONFIG_VFS_SUPPORT_TERMIOS From ec31f235e9fd91840ed3f19b54044d6247ef1d52 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Wed, 17 Jul 2019 09:59:21 +0200 Subject: [PATCH 2/2] docs: Correct and extend the documentation about VFS select() --- components/vfs/README.rst | 82 ++++++++++++++++++++++++++++++++------- components/vfs/vfs.c | 2 + 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/components/vfs/README.rst b/components/vfs/README.rst index 20bc9770e..c296e81a6 100644 --- a/components/vfs/README.rst +++ b/components/vfs/README.rst @@ -66,9 +66,29 @@ Case 2: API functions are declared with an extra context pointer (the FS driver Synchronous input/output multiplexing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you want to use synchronous input/output multiplexing by :cpp:func:`select` -then you need to register VFS with the functions :cpp:func:`start_select` and -:cpp:func:`end_select` similar to the following example: +Synchronous input/output multiplexing by :cpp:func:`select` is supported in the VFS component. The implementation +works in the following way. + +1. :cpp:func:`select` is called with file descriptors which could belong to various VFS drivers. +2. The file descriptors are divided into groups each belonging to one VFS driver. +3. The file descriptors belonging to non-socket VFS drivers are handed over to the given VFS drivers by :cpp:func:`start_select` + described later on this page. This function represents the driver-specific implementation of :cpp:func:`select` for + the given driver. This should be a non-blocking call which means the function should immediately return after setting up + the environment for checking events related to the given file descriptors. +4. The file descriptors belonging to the socket VFS driver are handed over to the socket driver by + :cpp:func:`socket_select` described later on this page. This is a blocking call which means that it will return only + if there is an event related to socket file descriptors or a non-socket driver signals :cpp:func:`socket_select` + to exit. +5. Results are collected from each VFS driver and all drivers are stopped by deinitiazation + of the environment for checking events. +6. The :cpp:func:`select` call ends and returns the appropriate results. + +Non-socket VFS drivers +"""""""""""""""""""""" + +If you want to use :cpp:func:`select` with a file descriptor belonging to a non-socket VFS driver +then you need to register the driver with functions :cpp:func:`start_select` and +:cpp:func:`end_select` similarly to the following example: .. highlight:: c @@ -81,25 +101,57 @@ then you need to register VFS with the functions :cpp:func:`start_select` and :cpp:func:`start_select` is called for setting up the environment for detection of read/write/error conditions on file descriptors belonging to the -given VFS. :cpp:func:`end_select` is called to stop/deinitialize/free the -environment which was setup by :cpp:func:`start_select`. Please refer to the +given VFS driver. + +:cpp:func:`end_select` is called to stop/deinitialize/free the +environment which was setup by :cpp:func:`start_select`. + +Please refer to the reference implementation for the UART peripheral in :component_file:`vfs/vfs_uart.c` and most particularly to the functions :cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select`, and -:cpp:func:`uart_end_select`. +:cpp:func:`uart_end_select` for more information. Please check the following examples that demonstrate the use of :cpp:func:`select` with VFS file descriptors: -- :example:`peripherals/uart_select` -- :example:`system/select` + - :example:`peripherals/uart/uart_select` + - :example:`system/select` -<<<<<<< HEAD -If :cpp:func:`select` is used for socket file descriptors only then one can -enable the :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option which can reduce the code -======= -If you use :cpp:func:`select` for socket file descriptors, you can enable the :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code ->>>>>>> afc2fdf27... Review all the files in the esp-idf's api_ref/storage directory -size and improve performance. +Socket VFS drivers +"""""""""""""""""" +A socket VFS driver is using its own internal implementation of :cpp:func:`select` and non-socket VFS drivers notify +it upon read/write/error conditions. + +A socket VFS driver needs to be registered with the following functions defined: + +.. highlight:: c + +:: + + // In definition of esp_vfs_t: + .socket_select = &lwip_select, + .get_socket_select_semaphore = &lwip_get_socket_select_semaphore, + .stop_socket_select = &lwip_stop_socket_select, + .stop_socket_select_isr = &lwip_stop_socket_select_isr, + // ... other members initialized + +:cpp:func:`socket_select` is the internal implementation of :cpp:func:`select` for the socket driver. It works only +with file descriptors belonging to the socket VFS. + +:cpp:func:`get_socket_select_semaphore` returns the signalization object (semaphore) which will be used in non-socket +drivers to stop the waiting in :cpp:func:`socket_select`. + +:cpp:func:`stop_socket_select` call is used to stop the waiting in :cpp:func:`socket_select` by passing the object +returned by :cpp:func:`get_socket_select_semaphore`. + +:cpp:func:`stop_socket_select_isr` has the same functionality as :cpp:func:`stop_socket_select` but it can be used +from ISR. + +Please see :component_file:`lwip/port/esp32/vfs_lwip.c` for a reference socket driver implementation using LWIP. + +.. note:: + If you use :cpp:func:`select` for socket file descriptors only then you can enable the + :envvar:`CONFIG_LWIP_USE_ONLY_LWIP_SELECT` option to reduce the code size and improve performance. Paths ----- diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 760d32728..d68103882 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -858,6 +858,8 @@ static void esp_vfs_log_fd_set(const char *fds_name, const fd_set *fds) int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { + // NOTE: Please see the "Synchronous input/output multiplexing" section of the ESP-IDF Programming Guide + // (API Reference -> Storage -> Virtual Filesystem) for a general overview of the implementation of VFS select(). int ret = 0; struct _reent* r = __getreent();