net: sockets: Implement select() call.
It's implemented on top of poll() anyway, and the current implementation of fd_set uses array of fd's underlyingly, which leads to O(n) complexity for FD_SET() and friends. The purpose of select() implementation is to allow to perform proof-of-concept port of 3rd-party code to Zephyr quickly. For efficiency, poll() should be used instead. Fixes: #11333 Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
f41a979df5
commit
3a0a9944c0
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
* Copyright (c) 2017-2018 Linaro Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -31,14 +31,21 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct timeval;
|
||||
|
||||
struct zsock_pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
typedef struct zsock_fd_set {
|
||||
u32_t bitset[(CONFIG_POSIX_MAX_FDS + 31) / 32];
|
||||
} zsock_fd_set;
|
||||
|
||||
/* Values are compatible with Linux */
|
||||
#define ZSOCK_POLLIN 1
|
||||
#define ZSOCK_POLLPRI 2
|
||||
#define ZSOCK_POLLOUT 4
|
||||
#define ZSOCK_POLLERR 8
|
||||
#define ZSOCK_POLLHUP 0x10
|
||||
|
@ -153,6 +160,19 @@ __syscall int zsock_fcntl(int sock, int cmd, int flags);
|
|||
|
||||
__syscall int zsock_poll(struct zsock_pollfd *fds, int nfds, int timeout);
|
||||
|
||||
/* select() API is inefficient, and implemented as inefficient wrapper on
|
||||
* top of poll(). Avoid select(), use poll directly().
|
||||
*/
|
||||
int zsock_select(int nfds, zsock_fd_set *readfds, zsock_fd_set *writefds,
|
||||
zsock_fd_set *exceptfds, struct timeval *timeout);
|
||||
|
||||
#define ZSOCK_FD_SETSIZE (sizeof(((zsock_fd_set *)0)->bitset) * 8)
|
||||
|
||||
void ZSOCK_FD_ZERO(zsock_fd_set *set);
|
||||
int ZSOCK_FD_ISSET(int fd, zsock_fd_set *set);
|
||||
void ZSOCK_FD_CLR(int fd, zsock_fd_set *set);
|
||||
void ZSOCK_FD_SET(int fd, zsock_fd_set *set);
|
||||
|
||||
int zsock_getsockopt(int sock, int level, int optname,
|
||||
void *optval, socklen_t *optlen);
|
||||
|
||||
|
@ -171,7 +191,11 @@ int zsock_getaddrinfo(const char *host, const char *service,
|
|||
struct zsock_addrinfo **res);
|
||||
|
||||
#if defined(CONFIG_NET_SOCKETS_POSIX_NAMES)
|
||||
|
||||
#define pollfd zsock_pollfd
|
||||
#define fd_set zsock_fd_set
|
||||
#define FD_SETSIZE ZSOCK_FD_SETSIZE
|
||||
|
||||
#if !defined(CONFIG_NET_SOCKETS_OFFLOAD)
|
||||
static inline int socket(int family, int type, int proto)
|
||||
{
|
||||
|
@ -235,6 +259,33 @@ static inline int poll(struct zsock_pollfd *fds, int nfds, int timeout)
|
|||
return zsock_poll(fds, nfds, timeout);
|
||||
}
|
||||
|
||||
static inline int select(int nfds, zsock_fd_set *readfds,
|
||||
zsock_fd_set *writefds, zsock_fd_set *exceptfds,
|
||||
struct timeval *timeout)
|
||||
{
|
||||
return zsock_select(nfds, readfds, writefds, exceptfds, timeout);
|
||||
}
|
||||
|
||||
static inline void FD_ZERO(zsock_fd_set *set)
|
||||
{
|
||||
ZSOCK_FD_ZERO(set);
|
||||
}
|
||||
|
||||
static inline int FD_ISSET(int fd, zsock_fd_set *set)
|
||||
{
|
||||
return ZSOCK_FD_ISSET(fd, set);
|
||||
}
|
||||
|
||||
static inline void FD_CLR(int fd, zsock_fd_set *set)
|
||||
{
|
||||
ZSOCK_FD_CLR(fd, set);
|
||||
}
|
||||
|
||||
static inline void FD_SET(int fd, zsock_fd_set *set)
|
||||
{
|
||||
ZSOCK_FD_SET(fd, set);
|
||||
}
|
||||
|
||||
static inline int getsockopt(int sock, int level, int optname,
|
||||
void *optval, socklen_t *optlen)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@ if(NOT CONFIG_NET_SOCKETS_OFFLOAD)
|
|||
zephyr_sources(
|
||||
getaddrinfo.c
|
||||
sockets.c
|
||||
sockets_select.c
|
||||
)
|
||||
zephyr_sources_ifdef(CONFIG_NET_SOCKETS_SOCKOPT_TLS sockets_tls.c)
|
||||
endif()
|
||||
|
|
200
subsys/net/lib/sockets/sockets_select.c
Normal file
200
subsys/net/lib/sockets/sockets_select.c
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Linaro Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <net/socket.h>
|
||||
|
||||
/* Get size, in elements, of an array within a struct. */
|
||||
#define STRUCT_MEMBER_ARRAY_SIZE(type, field) ARRAY_SIZE(((type *)0)->field)
|
||||
|
||||
/* Returns results in word_idx and bit_mask "output" params */
|
||||
#define FD_SET_CALC_OFFSETS(set, word_idx, bit_mask) { \
|
||||
unsigned int b_idx = fd % (sizeof(set->bitset[0]) * 8); \
|
||||
word_idx = fd / (sizeof(set->bitset[0]) * 8); \
|
||||
bit_mask = 1 << b_idx; \
|
||||
}
|
||||
|
||||
void ZSOCK_FD_ZERO(zsock_fd_set *set)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(set->bitset); i++) {
|
||||
set->bitset[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ZSOCK_FD_ISSET(int fd, zsock_fd_set *set)
|
||||
{
|
||||
u32_t word_idx, bit_mask;
|
||||
|
||||
if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
|
||||
|
||||
return (set->bitset[word_idx] & bit_mask) != 0;
|
||||
}
|
||||
|
||||
void ZSOCK_FD_CLR(int fd, zsock_fd_set *set)
|
||||
{
|
||||
u32_t word_idx, bit_mask;
|
||||
|
||||
if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
|
||||
|
||||
set->bitset[word_idx] &= ~bit_mask;
|
||||
}
|
||||
|
||||
void ZSOCK_FD_SET(int fd, zsock_fd_set *set)
|
||||
{
|
||||
u32_t word_idx, bit_mask;
|
||||
|
||||
if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
|
||||
|
||||
set->bitset[word_idx] |= bit_mask;
|
||||
}
|
||||
|
||||
int zsock_select(int nfds, zsock_fd_set *readfds, zsock_fd_set *writefds,
|
||||
zsock_fd_set *exceptfds, struct timeval *timeout)
|
||||
{
|
||||
struct pollfd pfds[CONFIG_NET_SOCKETS_POLL_MAX];
|
||||
int i, res, poll_timeout;
|
||||
int num_pfds = 0;
|
||||
int num_selects = 0;
|
||||
int fd_no = 0;
|
||||
|
||||
for (i = 0; i < STRUCT_MEMBER_ARRAY_SIZE(zsock_fd_set, bitset); i++) {
|
||||
u32_t bit_mask = 1;
|
||||
u32_t read_mask = 0, write_mask = 0, except_mask = 0;
|
||||
u32_t ored_mask;
|
||||
|
||||
if (readfds != NULL) {
|
||||
read_mask = readfds->bitset[i];
|
||||
}
|
||||
|
||||
if (writefds != NULL) {
|
||||
write_mask = writefds->bitset[i];
|
||||
}
|
||||
|
||||
if (exceptfds != NULL) {
|
||||
except_mask = exceptfds->bitset[i];
|
||||
}
|
||||
|
||||
ored_mask = read_mask | write_mask | except_mask;
|
||||
if (ored_mask == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
if (ored_mask & bit_mask) {
|
||||
int events = 0;
|
||||
|
||||
if (num_pfds >= ARRAY_SIZE(pfds)) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_mask & bit_mask) {
|
||||
events |= ZSOCK_POLLIN;
|
||||
}
|
||||
|
||||
if (write_mask & bit_mask) {
|
||||
events |= ZSOCK_POLLOUT;
|
||||
}
|
||||
|
||||
if (except_mask & bit_mask) {
|
||||
events |= ZSOCK_POLLPRI;
|
||||
}
|
||||
|
||||
pfds[num_pfds].fd = fd_no;
|
||||
pfds[num_pfds++].events = events;
|
||||
}
|
||||
|
||||
bit_mask <<= 1;
|
||||
fd_no++;
|
||||
} while (bit_mask != 0);
|
||||
}
|
||||
|
||||
poll_timeout = -1;
|
||||
if (timeout != NULL) {
|
||||
poll_timeout = timeout->tv_sec * 1000
|
||||
+ timeout->tv_usec / 1000;
|
||||
}
|
||||
|
||||
res = zsock_poll(pfds, num_pfds, poll_timeout);
|
||||
if (res == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (readfds != NULL) {
|
||||
FD_ZERO(readfds);
|
||||
}
|
||||
|
||||
if (writefds != NULL) {
|
||||
FD_ZERO(writefds);
|
||||
}
|
||||
|
||||
if (exceptfds != NULL) {
|
||||
FD_ZERO(exceptfds);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_pfds && res > 0; i++) {
|
||||
short revents = pfds[i].revents;
|
||||
int fd = pfds[i].fd;
|
||||
|
||||
if (revents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* POSIX: "EBADF: One or more of the file descriptor sets
|
||||
* specified a file descriptor that is not a valid open
|
||||
* file descriptor."
|
||||
* So, unlike poll(), a single invalid fd aborts the entire
|
||||
* select().
|
||||
*/
|
||||
if (revents & ZSOCK_POLLNVAL) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (revents & ZSOCK_POLLIN) {
|
||||
if (readfds != NULL) {
|
||||
ZSOCK_FD_SET(fd, readfds);
|
||||
num_selects++;
|
||||
}
|
||||
}
|
||||
|
||||
if (revents & ZSOCK_POLLOUT) {
|
||||
if (writefds != NULL) {
|
||||
ZSOCK_FD_SET(fd, writefds);
|
||||
num_selects++;
|
||||
}
|
||||
}
|
||||
|
||||
/* It's unclear if HUP/ERR belong here. At least not ignore
|
||||
* them. Zephyr doesn't use HUP and barely use ERR so far.
|
||||
*/
|
||||
if (revents & (ZSOCK_POLLPRI | ZSOCK_POLLHUP | ZSOCK_POLLERR)) {
|
||||
if (exceptfds != NULL) {
|
||||
ZSOCK_FD_SET(fd, exceptfds);
|
||||
num_selects++;
|
||||
}
|
||||
}
|
||||
|
||||
res--;
|
||||
}
|
||||
|
||||
return num_selects;
|
||||
}
|
Loading…
Reference in a new issue