mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'fix/freertos_sim_nonblock_select' into 'master'
fix(freertos): Made select function non-blocking on Linux target (GitHub PR) Closes IDFGH-13569 and IDFGH-13498 See merge request espressif/esp-idf!33331
This commit is contained in:
commit
4dd715922f
@ -1,56 +1,95 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
#include <pthread.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <sys/types.h>
|
#include <assert.h>
|
||||||
#include "esp_err.h"
|
#include <sys/select.h>
|
||||||
#include "errno.h"
|
#include <errno.h>
|
||||||
|
|
||||||
/** This module addresses the FreeRTOS simulator's coexistence with linux system calls from user apps.
|
/** This module addresses the FreeRTOS simulator's coexistence with Linux system calls from user apps.
|
||||||
* It's only included when building without lwIP, so we need to use linux system's select() which would receive
|
* It wraps select so that it doesn't block the FreeRTOS task calling it, so that the
|
||||||
* EINTR event on every FreeRTOS interrupt; we workaround this problem by wrapping select()
|
* scheduler will allow lower priority tasks to run.
|
||||||
* to bypass and silence these events.
|
* Without the wrapper, most components such as ESP-MQTT block lower priority tasks from running at all.
|
||||||
*/
|
*/
|
||||||
typedef int (*select_func_t) (int fd, fd_set * rfds, fd_set * wfds, fd_set *efds, struct timeval *tval);
|
typedef int (*select_func_t)(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tval);
|
||||||
|
|
||||||
static inline int64_t get_us(void)
|
int select(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tval)
|
||||||
{
|
{
|
||||||
struct timespec spec;
|
static select_func_t s_real_select = NULL;
|
||||||
clock_gettime(CLOCK_REALTIME, &spec);
|
TickType_t end_ticks = portMAX_DELAY;
|
||||||
return spec.tv_nsec / 1000 + spec.tv_sec * 1000000;
|
fd_set o_rfds, o_wfds, o_efds;
|
||||||
}
|
|
||||||
|
|
||||||
int select (int fd, fd_set * rfds, fd_set * wfds, fd_set *efds, struct timeval *tval)
|
// Lookup the select symbol
|
||||||
{
|
if (s_real_select == NULL) {
|
||||||
int ret;
|
s_real_select = (select_func_t)dlsym(RTLD_NEXT, "select");
|
||||||
struct timeval *tv = tval;
|
assert(s_real_select); // abort() if we cannot locate the symbol
|
||||||
struct timeval timeval_local = {};
|
|
||||||
int64_t start = 0;
|
|
||||||
int64_t timeout_us = 0;
|
|
||||||
select_func_t real_select = (select_func_t) dlsym(RTLD_NEXT, "select");
|
|
||||||
if (tv != NULL) {
|
|
||||||
start = get_us();
|
|
||||||
timeout_us = tval->tv_sec * 1000000 + tval->tv_usec;
|
|
||||||
timeval_local.tv_sec = tval->tv_sec;
|
|
||||||
timeval_local.tv_usec = tval->tv_usec;
|
|
||||||
tv = &timeval_local; // this (tv != NULL) indicates that we should handle timeouts
|
|
||||||
}
|
}
|
||||||
while ((ret = real_select(fd, rfds, wfds, efds, tv)) < 0 && errno == EINTR) {
|
|
||||||
if (tv != NULL) {
|
// Calculate the end_ticks if a timeout is provided
|
||||||
int64_t now = get_us();
|
if (tval != NULL) {
|
||||||
timeout_us -= now - start;
|
end_ticks = xTaskGetTickCount() + pdMS_TO_TICKS(tval->tv_sec * 1000 + tval->tv_usec / 1000);
|
||||||
if (timeout_us < 0) {
|
}
|
||||||
errno = 0;
|
|
||||||
ret = 0;
|
// Preserve the original FD sets as select call will change them
|
||||||
break;
|
if (rfds) {
|
||||||
}
|
o_rfds = *rfds;
|
||||||
start = now;
|
}
|
||||||
tv->tv_usec = timeout_us % 1000000;
|
if (wfds) {
|
||||||
tv->tv_sec = timeout_us / 1000000;
|
o_wfds = *wfds;
|
||||||
|
}
|
||||||
|
if (efds) {
|
||||||
|
o_efds = *efds;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Restore original FD sets before the select call
|
||||||
|
if (rfds) {
|
||||||
|
*rfds = o_rfds;
|
||||||
}
|
}
|
||||||
|
if (wfds) {
|
||||||
|
*wfds = o_wfds;
|
||||||
|
}
|
||||||
|
if (efds) {
|
||||||
|
*efds = o_efds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call select with a zero timeout to avoid blocking
|
||||||
|
struct timeval zero_tv = {0, 0};
|
||||||
|
int ret = s_real_select(fd, rfds, wfds, efds, &zero_tv);
|
||||||
|
|
||||||
|
// Return on success
|
||||||
|
if (ret > 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return on any error except EINTR
|
||||||
|
if (ret == -1 && errno != EINTR) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep for maximum 10 tick(s) to allow other tasks to run.
|
||||||
|
* This can be any value greater than zero.
|
||||||
|
* 10 is a good trade-off between CPU time usage and timeout resolution.
|
||||||
|
*/
|
||||||
|
const TickType_t max_sleep_ticks = 10;
|
||||||
|
TickType_t sleep_ticks = max_sleep_ticks;
|
||||||
|
|
||||||
|
if (tval != NULL) {
|
||||||
|
TickType_t now_ticks = xTaskGetTickCount();
|
||||||
|
if (now_ticks >= end_ticks) {
|
||||||
|
errno = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Sleep for the remaining time or a maximum of 10 tick
|
||||||
|
TickType_t remaining_ticks = end_ticks - now_ticks;
|
||||||
|
sleep_ticks = (remaining_ticks < max_sleep_ticks) ? remaining_ticks : max_sleep_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(sleep_ticks);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user