1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/posix/select/posix_select.c
2022-03-10 14:24:57 +01:00

145 lines
3.9 KiB
C

/*
* Copyright (C) 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
* @file
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
*/
#include <errno.h>
#include <stdbool.h>
#include <sys/select.h>
#include "thread_flags.h"
#include "vfs.h"
#include "ztimer64.h"
#include "timex.h"
#if IS_USED(MODULE_POSIX_SOCKETS)
extern bool posix_socket_is(int fd);
extern unsigned posix_socket_avail(int fd);
extern void posix_socket_select(int fd);
#else /* MODULE_POSIX_SOCKETS */
static inline bool posix_socket_is(int fd)
{
(void)fd;
return false;
}
static inline unsigned posix_socket_avail(int fd)
{
(void)fd;
return 0;
}
static inline void posix_socket_select(int fd)
{
(void)fd;
return 0;
}
#endif /* IS_USED(MODULE_POSIX_SOCKETS) */
static int _set_timeout(ztimer64_t *timeout_timer, struct timeval *timeout,
uint32_t offset, bool *wait)
{
if (timeout != NULL) {
uint64_t t = ((uint64_t)(timeout->tv_sec * US_PER_SEC) +
timeout->tv_usec);
/* check for potential underflow before subtracting offset */
if ((t == 0) || (offset > t)) {
*wait = false;
return 0;
}
t -= offset;
if (t > UINT32_MAX) {
errno = EINVAL;
/* don't have timer set yet so go to end */
return -1;
}
else {
ztimer64_set_timeout_flag(ZTIMER64_USEC, timeout_timer, (uint32_t)t);
}
}
return 0;
}
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds,
struct timeval *timeout)
{
uint32_t start_time = ztimer64_now(ZTIMER64_USEC);
fd_set ret_readfds;
ztimer64_t timeout_timer;
int fds_set = 0;
bool wait = true;
FD_ZERO(&ret_readfds);
/* TODO ignored writefds and errorfds for now since there is no point for
* them with sockets */
if ((nfds >= FD_SETSIZE) || ((unsigned)nfds >= VFS_MAX_OPEN_FILES)) {
errno = EINVAL;
return -1;
}
for (int i = 0; i < nfds; i++) {
if ((readfds != NULL) && FD_ISSET(i, readfds)) {
if (!posix_socket_is(i)) {
errno = EBADF;
return -1;
}
if (posix_socket_avail(i) > 0) {
FD_SET(i, &ret_readfds);
fds_set++;
wait = false;
}
else {
posix_socket_select(i);
}
}
if ((writefds != NULL) && FD_ISSET(i, writefds) &&
!posix_socket_is(i)) {
errno = EBADF;
return -1;
}
if ((errorfds != NULL) && FD_ISSET(i, errorfds) &&
!posix_socket_is(i)) {
errno = EBADF;
return -1;
}
}
while (wait) {
if (_set_timeout(&timeout_timer, timeout,
ztimer64_now(ZTIMER64_USEC) - start_time, &wait) < 0) {
return -1;
}
if (!wait) {
errno = EINTR;
return -1;
}
thread_flags_t tflags = thread_flags_wait_any(POSIX_SELECT_THREAD_FLAG |
THREAD_FLAG_TIMEOUT);
if (tflags & POSIX_SELECT_THREAD_FLAG) {
for (int i = 0; i < nfds; i++) {
if (FD_ISSET(i, readfds)) {
if (posix_socket_avail(i) > 0) {
FD_SET(i, &ret_readfds);
fds_set++;
wait = false;
}
}
}
}
else if (tflags & THREAD_FLAG_TIMEOUT) {
errno = EINTR;
return -1;
}
ztimer64_remove(ZTIMER64_USEC, &timeout_timer);
}
*readfds = ret_readfds;
return fds_set;
}