1
0
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:
Martine S. Lenders 2019-12-13 13:01:30 +01:00
parent af24c539d0
commit 6ba6740231
No known key found for this signature in database
GPG Key ID: CCD317364F63286F
7 changed files with 376 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -14,3 +14,9 @@
* </a>
* @ingroup sys
*/
/**
* @defgroup config_posix POSIX wrapper compile configurations
* @ingroup posix
* @ingroup config
*/

View 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 */
/** @} */

View File

@ -0,0 +1,3 @@
MODULE = posix_select
include $(RIOTBASE)/Makefile.base

View 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;
}

View File

@ -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;
}
/**
* @}
*/