mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
posix_select: initial import of select()
function
This commit is contained in:
parent
af24c539d0
commit
6ba6740231
@ -470,6 +470,15 @@ ifneq (,$(filter newlib,$(USEMODULE)))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter posix_select,$(USEMODULE)))
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
USEMODULE += sock_async
|
||||
endif
|
||||
USEMODULE += core_thread_flags
|
||||
USEMODULE += posix_headers
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
USEMODULE += bitfield
|
||||
USEMODULE += random
|
||||
|
@ -10,6 +10,9 @@ endif
|
||||
ifneq (,$(filter posix_inet,$(USEMODULE)))
|
||||
DIRS += posix/inet
|
||||
endif
|
||||
ifneq (,$(filter posix_select,$(USEMODULE)))
|
||||
DIRS += posix/select
|
||||
endif
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
DIRS += posix/semaphore
|
||||
endif
|
||||
|
@ -14,3 +14,9 @@
|
||||
* </a>
|
||||
* @ingroup sys
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup config_posix POSIX wrapper compile configurations
|
||||
* @ingroup posix
|
||||
* @ingroup config
|
||||
*/
|
||||
|
172
sys/posix/include/sys/select.h
Normal file
172
sys/posix/include/sys/select.h
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup posix_select POSIX select
|
||||
* @ingroup posix
|
||||
* @brief Select implementation for RIOT
|
||||
* @see [The Open Group Base Specification Issue 7]
|
||||
* (https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/)
|
||||
* @todo Omitted from original specification for now:
|
||||
* - Inclusion of `<signal.h>`; no POSIX signal handling implemented
|
||||
* in RIOT yet
|
||||
* - `pselect()` as it uses `sigset_t` from `<signal.h>`
|
||||
* - handling of the `writefds` and `errorfds` parameters of `select()`
|
||||
* @todo Currently, only [sockets](@ref posix_sockets) are supported
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Select types
|
||||
* @see [The Open Group Base Specification Issue 7, 2018 edition,
|
||||
* <sys/select.h>](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/sys_select.h)
|
||||
*/
|
||||
|
||||
#ifndef SYS_SELECT_H
|
||||
#define SYS_SELECT_H
|
||||
|
||||
#include <string.h>
|
||||
/* prevent cyclic dependency with newlib's `sys/types.h` */
|
||||
#if defined(MODULE_NEWLIB) && !defined(CPU_ESP32) && !defined(CPU_ESP8266)
|
||||
#include <sys/_timeval.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "bitfield.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup config_posix
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Maximum number of file descriptors in an `fd_set` structure.
|
||||
*
|
||||
* @note Should have at least the same value as VFS_MAX_OPEN_FILES.
|
||||
*
|
||||
* Config-exposed version
|
||||
*/
|
||||
#ifndef CONFIG_POSIX_FD_SET_SIZE
|
||||
#define CONFIG_POSIX_FD_SET_SIZE (16)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief @ref core_thread_flags for POSIX select
|
||||
*/
|
||||
#define POSIX_SELECT_THREAD_FLAG (1U << 3)
|
||||
|
||||
/* ESP's newlib has this already defined in `sys/types.h` */
|
||||
#if !defined(CPU_ESP32) && !defined(CPU_ESP8266)
|
||||
/**
|
||||
* @brief Maximum number of file descriptors in an `fd_set` structure.
|
||||
*
|
||||
* POSIX-compliant version.
|
||||
*/
|
||||
#define FD_SETSIZE (CONFIG_POSIX_FD_SET_SIZE)
|
||||
|
||||
/**
|
||||
* @brief The `fd_set` structure
|
||||
*/
|
||||
typedef struct {
|
||||
BITFIELD(fds, FD_SETSIZE); /**< Bit-field to represent the set of file
|
||||
* descriptors */
|
||||
} fd_set;
|
||||
|
||||
/**
|
||||
* @brief Removes a file descriptor from an `fd_set` if it is a member
|
||||
*
|
||||
* @param[in] fd A file descriptor
|
||||
* @param[in] fdsetp An `fd_set`
|
||||
*/
|
||||
static inline void FD_CLR(int fd, fd_set *fdsetp)
|
||||
{
|
||||
bf_unset(fdsetp->fds, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a file descriptor is a member of an `fd_set`
|
||||
*
|
||||
* @param[in] fd A file descriptor
|
||||
* @param[in] fdsetp An `fd_set`
|
||||
*
|
||||
* @return non-zero value, if @p fd is a member of @p fdsetp
|
||||
* @return 0, if @p fd is not a member of @p fdsetp
|
||||
*/
|
||||
static inline int FD_ISSET(int fd, fd_set *fdsetp)
|
||||
{
|
||||
return (int)bf_isset(fdsetp->fds, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a file descriptor from an `fd_set` if it is not already a
|
||||
* member
|
||||
*
|
||||
* @param[in] fd A file descriptor
|
||||
* @param[in] fdsetp An `fd_set`
|
||||
*/
|
||||
static inline void FD_SET(int fd, fd_set *fdsetp)
|
||||
{
|
||||
bf_set(fdsetp->fds, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the descriptor set as an empty set
|
||||
*
|
||||
* @param[in] fdsetp An `fd_set`
|
||||
*/
|
||||
static inline void FD_ZERO(fd_set *fdsetp)
|
||||
{
|
||||
memset(fdsetp->fds, 0, sizeof(fdsetp->fds));
|
||||
}
|
||||
#endif /* !defined(CPU_ESP32) && !defined(CPU_ESP8266) */
|
||||
|
||||
/**
|
||||
* @brief Examines the given file descriptor sets if they are ready for their
|
||||
* respective operation.
|
||||
*
|
||||
* @param[in] nfds The range of descriptors tested. The first @p nfds
|
||||
* descriptors shall be checked in each set; that is,
|
||||
* the descriptors from zero through @p nfds - 1 in the
|
||||
* descriptor sets shall be examined.
|
||||
* @param[in,out] readfds The set of file descriptors to be checked for being
|
||||
* ready to read. Indicates on output which file
|
||||
* descriptors are ready to read. May be NULL to check
|
||||
* no file descriptors.
|
||||
* @param[in,out] writefds The set of file descriptors to be checked for being
|
||||
* ready to write. Indicates on output which file
|
||||
* descriptors are ready to write. May be NULL to check
|
||||
* no file descriptors.
|
||||
* **As only sockets are supported for now, these will
|
||||
* be ignored**
|
||||
* @param[in,out] errorfds The set of file descriptors to be checked for being
|
||||
* error conditions pending. Indicates on output which
|
||||
* file descriptors have error conditions pending. May
|
||||
* be NULL to check no file descriptors.
|
||||
* **As only sockets are supported for now, these will
|
||||
* be ignored**
|
||||
* @param[in] timeout Timeout for select to block until one or more of the
|
||||
* checked file descriptors is ready. Set timeout
|
||||
* to all-zero to return immediately without blocking.
|
||||
* May be NULL to block indefinitely.
|
||||
*
|
||||
* @return number of members added to the file descriptor sets on success.
|
||||
* @return -1 on error, `errno` is set to indicate the error.
|
||||
*/
|
||||
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds,
|
||||
struct timeval *timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SYS_SELECT_H */
|
||||
/** @} */
|
3
sys/posix/select/Makefile
Normal file
3
sys/posix/select/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = posix_select
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
143
sys/posix/select/posix_select.c
Normal file
143
sys/posix/select/posix_select.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 "xtimer.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(xtimer_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 {
|
||||
xtimer_set_timeout_flag(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 = xtimer_now_usec();
|
||||
fd_set ret_readfds;
|
||||
xtimer_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,
|
||||
xtimer_now_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;
|
||||
}
|
||||
xtimer_remove(&timeout_timer);
|
||||
}
|
||||
*readfds = ret_readfds;
|
||||
return fds_set;
|
||||
}
|
@ -41,6 +41,12 @@
|
||||
#if IS_USED(MODULE_SOCK_ASYNC)
|
||||
#include "net/sock/async.h"
|
||||
#endif
|
||||
#if IS_USED(MODULE_POSIX_SELECT)
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "thread_flags.h"
|
||||
#endif
|
||||
|
||||
/* enough to create sockets both with socket() and accept() */
|
||||
#define _ACTUAL_SOCKET_POOL_SIZE (SOCKET_POOL_SIZE + \
|
||||
@ -86,6 +92,9 @@ typedef struct {
|
||||
#endif
|
||||
#if IS_USED(MODULE_SOCK_ASYNC)
|
||||
atomic_uint available;
|
||||
#endif
|
||||
#if IS_USED(MODULE_POSIX_SELECT)
|
||||
thread_t *selecting_thread;
|
||||
#endif
|
||||
sock_tcp_ep_t local; /* to store bind before connect/listen */
|
||||
} socket_t;
|
||||
@ -115,6 +124,9 @@ static socket_t *_get_free_socket(void)
|
||||
if (_socket_pool[i].domain == AF_UNSPEC) {
|
||||
#if IS_USED(MODULE_SOCK_ASYNC)
|
||||
atomic_init(&_socket_pool[i].available, 0U);
|
||||
#endif
|
||||
#if IS_USED(MODULE_POSIX_SELECT)
|
||||
_socket_pool[i].selecting_thread = NULL;
|
||||
#endif
|
||||
return &_socket_pool[i];
|
||||
}
|
||||
@ -359,8 +371,10 @@ static void _async_cb(void *sock, sock_async_flags_t type,
|
||||
if (type & SOCK_ASYNC_MSG_RECV) {
|
||||
atomic_fetch_add(&socket->available, 1);
|
||||
#if IS_USED(MODULE_POSIX_SELECT)
|
||||
thread_flags_set(sock->socket->selecting_thread,
|
||||
POSIX_SELECT_THREAD_FLAG);
|
||||
if (socket->selecting_thread) {
|
||||
thread_flags_set(socket->selecting_thread,
|
||||
POSIX_SELECT_THREAD_FLAG);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1169,6 +1183,30 @@ unsigned posix_socket_avail(int fd)
|
||||
#endif
|
||||
}
|
||||
|
||||
int posix_socket_select(int fd)
|
||||
{
|
||||
#if IS_USED(MODULE_POSIX_SELECT)
|
||||
socket_t *socket = _get_socket(fd);
|
||||
|
||||
if (socket != NULL) {
|
||||
if (socket->sock == NULL) { /* socket is not connected */
|
||||
int res;
|
||||
|
||||
/* bind implicitly */
|
||||
if ((res = _bind_connect(socket, NULL, 0)) < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
socket->selecting_thread = (thread_t *)sched_active_thread;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
(void)fd;
|
||||
#endif
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user