esp-idf/components/freertos/esp_additions/FreeRTOSSimulator_wrappers.c
Ivan Grokhotkov f5cd3b7ff4
fix(freertos): override select function without relying on -Wl,--wrap
This commit changes the approach used to override select function
in FreeRTOS simulator.

The previous approach relied on '--wrap', which is a feature of GNU
linker that performs symbol substitution at link time. Sadly this
feature is not present in macOS linker.

The alternative solution is to define 'select' wrapper as a regular
(exported) symbol, preventing it from being loaded from system
libraries. To call the "real" select implementation we use dlsym
function and find the function pointer at run time. This solution
works on both Linux and macOS.
2024-01-23 16:57:38 +01:00

57 lines
1.9 KiB
C

/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <pthread.h>
#include <dlfcn.h>
#include <sys/types.h>
#include "esp_err.h"
#include "errno.h"
/** 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
* EINTR event on every FreeRTOS interrupt; we workaround this problem by wrapping select()
* to bypass and silence these events.
*/
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)
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000 + spec.tv_sec * 1000000;
}
int select (int fd, fd_set * rfds, fd_set * wfds, fd_set *efds, struct timeval *tval)
{
int ret;
struct timeval *tv = tval;
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) {
int64_t now = get_us();
timeout_us -= now - start;
if (timeout_us < 0) {
errno = 0;
ret = 0;
break;
}
start = now;
tv->tv_usec = timeout_us % 1000000;
tv->tv_sec = timeout_us / 1000000;
}
}
return ret;
}