/*
 * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <pthread.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.
 */
extern int __real_select (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 __wrap_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;
    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;
}