[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [GSoC 2017] Number for sockets
From: |
Kalle Olavi Niemitalo |
Subject: |
Re: [GSoC 2017] Number for sockets |
Date: |
Sun, 13 Aug 2017 11:49:46 +0300 |
User-agent: |
Gnus/5.110007 (No Gnus v0.7) Emacs/23.0.51 (gnu/linux) |
Kalle Olavi Niemitalo <kon@iki.fi> writes:
> The implementation effort might be 4 hours, not including testing
> and publication.
That was very accurate. The following builds OK (also with
LWIP_POLL=1 if you add #include <poll.h> to cc.h) but I have not
run it.
Why do the files have CRLF in the lwip-hurd repository?
They don't in the lwip repository.
commit ad7fd88fdf98bd22c734269ce40d388a6ff351a5
Author: Kalle Olavi Niemitalo <kon@iki.fi>
AuthorDate: 2017-08-13 11:08:16 +0300
Commit: Kalle Olavi Niemitalo <kon@iki.fi>
CommitDate: 2017-08-13 11:32:06 +0300
Add lwip_poll
diff --git a/api/sockets.c b/api/sockets.c
index dede1f0..86b44ab 100644
--- a/api/sockets.c
+++ b/api/sockets.c
@@ -244,6 +244,12 @@ struct lwip_select_cb {
fd_set *writeset;
/** unimplemented: exceptset passed to select */
fd_set *exceptset;
+#if LWIP_POLL
+ /** fds passed to poll; NULL if select */
+ struct pollfd *poll_fds;
+ /** nfds passed to poll; 0 if select */
+ nfds_t poll_nfds;
+#endif
/** don't signal the same semaphore twice: set to 1 when signalled */
int sem_signalled;
/** semaphore to wake up a task waiting for select */
@@ -1367,6 +1373,51 @@ lwip_writev(int s, const struct iovec *iov, int iovcnt)
return lwip_sendmsg(s, &msg, 0);
}
+/* Add select_cb to select_cb_list. */
+static void
+lwip_link_select_cb(struct lwip_select_cb *select_cb)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Protect the select_cb_list */
+ SYS_ARCH_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb->next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = select_cb;
+ }
+ select_cb_list = &select_cb;
+ /* Increasing this counter tells event_callback that the list has changed. */
+ select_cb_ctr++;
+
+ /* Now we can safely unprotect */
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/* Remove select_cb from select_cb_list. */
+void
+lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Take us off the list */
+ SYS_ARCH_PROTECT(lev);
+ if (select_cb->next != NULL) {
+ select_cb->next->prev = select_cb->prev;
+ }
+ if (select_cb_list == &select_cb) {
+ LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
+ select_cb_list = select_cb->next;
+ } else {
+ LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
+ select_cb->prev->next = select_cb->next;
+ }
+ /* Increasing this counter tells event_callback that the list has changed. */
+ select_cb_ctr++;
+ SYS_ARCH_UNPROTECT(lev);
+}
+
/**
* Go through the readset and writeset lists and see which socket of the
sockets
* set in the sets has events. On return, readset, writeset and exceptset have
@@ -1505,6 +1556,10 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set
*writeset, fd_set *exceptset,
select_cb.readset = readset;
select_cb.writeset = writeset;
select_cb.exceptset = exceptset;
+#if LWIP_POLL
+ select_cb.poll_fds = NULL;
+ select_cb.poll_nfds = 0;
+#endif
select_cb.sem_signalled = 0;
#if LWIP_NETCONN_SEM_PER_THREAD
select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
@@ -1516,20 +1571,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set
*writeset, fd_set *exceptset,
}
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
- /* Protect the select_cb_list */
- SYS_ARCH_PROTECT(lev);
-
- /* Put this select_cb on top of list */
- select_cb.next = select_cb_list;
- if (select_cb_list != NULL) {
- select_cb_list->prev = &select_cb;
- }
- select_cb_list = &select_cb;
- /* Increasing this counter tells event_callback that the list has changed.
*/
- select_cb_ctr++;
-
- /* Now we can safely unprotect */
- SYS_ARCH_UNPROTECT(lev);
+ lwip_link_select_cb(&select_cb);
/* Increase select_waiting for each socket we are interested in */
maxfdp2 = maxfdp1;
@@ -1602,21 +1644,7 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set
*writeset, fd_set *exceptset,
SYS_ARCH_UNPROTECT(lev);
}
}
- /* Take us off the list */
- SYS_ARCH_PROTECT(lev);
- if (select_cb.next != NULL) {
- select_cb.next->prev = select_cb.prev;
- }
- if (select_cb_list == &select_cb) {
- LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
- select_cb_list = select_cb.next;
- } else {
- LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
- select_cb.prev->next = select_cb.next;
- }
- /* Increasing this counter tells event_callback that the list has changed.
*/
- select_cb_ctr++;
- SYS_ARCH_UNPROTECT(lev);
+ lwip_unlink_select_cb(&select_cb);
#if LWIP_NETCONN_SEM_PER_THREAD
if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT)))
{
@@ -1661,6 +1689,247 @@ return_copy_fdsets:
return nready;
}
+#if LWIP_POLL
+
+/** Options for the lwip_pollscan function. */
+enum lwip_pollscan_opts
+{
+ /** Clear revents in each struct pollfd. */
+ LWIP_POLLSCAN_CLEAR = 1,
+
+ /** Increment select_waiting in each struct lwip_sock. */
+ LWIP_POLLSCAN_INC_WAIT = 2,
+
+ /** Decrement select_waiting in each struct lwip_sock. */
+ LWIP_POLLSCAN_DEC_WAIT = 4
+};
+
+/**
+ * Update revents in each struct pollfd.
+ * Optionally update select_waiting in struct lwip_sock.
+ *
+ * @param fds array of structures to update
+ * @param nfds number of structures in fds
+ * @param opts what to update and how
+ * @return number of structures that have revents != 0
+ */
+static int
+lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
+{
+ int nready = 0;
+ nfds_t fdi;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Go through each struct pollfd in the array. */
+ for (fdi = 0; fdi < nfds; fdi++) {
+ if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
+ fds[fdi].revents = 0;
+ }
+
+ /* Negative fd means the caller wants us to ignore this struct.
+ POLLNVAL means we already detected that the fd is invalid;
+ if another thread has since opened a new socket with that fd,
+ we must not use that socket. */
+ if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(fds[fdi].fd);
+ if (sock != NULL) {
+ void* lastdata = sock->lastdata;
+ s16_t rcvevent = sock->rcvevent;
+ u16_t sendevent = sock->sendevent;
+ u16_t errevent = sock->errevent;
+ int err = sock->err;
+
+ if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
+ /* @todo: what if this is a new socket (reallocated?) in this case,
+ select_waiting-- would be wrong (a global 'sockalloc' counter,
+ stored per socket could help; the POLLNVAL checks reduce the
+ risk but do not eliminate it) */
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting > 0) {
+ sock->select_waiting--;
+ }
+ }
+
+ SYS_ARCH_UNPROTECT(lev);
+
+ /*
+ * FIXME: Workaround for handling the particular case when iioctl
+ * operations or fsysopts remove and add new interfaces while there are
+ * opened connections.
+ */
+ if (err != ECONNABORTED) {
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) ||
(rcvevent > 0))) {
+ fds[fdi].revents |= POLLIN;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for
reading\n", fds[fdi].fd));
+ }
+ /* See if netconn of this socket is ready for write */
+ if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
+ fds[fdi].revents |= POLLOUT;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for
writing\n", fds[fdi].fd));
+ }
+ /* See if netconn of this socket had an error */
+ if (errevent != 0) {
+ /* POLLERR is output only. */
+ fds[fdi].revents |= POLLERR;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for
exception\n", fds[fdi].fd));
+ }
+ }
+ } else {
+ /* Not a valid socket */
+ SYS_ARCH_UNPROTECT(lev);
+ /* POLLNVAL is output only. */
+ fds[fdi].revents |= POLLNVAL;
+ /* continue on to next struct pollfd in array */
+ }
+ }
+
+ /* Will return the number of structures that have events,
+ not the number of events. */
+ if (fds[fdi].revents != 0) {
+ nready++;
+ }
+ }
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+int
+lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ struct lwip_select_cb select_cb;
+ nfds_t fdi;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
+ (void*)fds, (int)nfds, timeout));
+
+ /* Go through each struct pollfd to count number of structures
+ which currently match */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ if (timeout == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
+ goto return_success;
+ }
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ select_cb.next = NULL;
+ select_cb.prev = NULL;
+ select_cb.readset = NULL;
+ select_cb.writeset = NULL;
+ select_cb.exceptset = NULL;
+ select_cb.poll_fds = fds;
+ select_cb.poll_nfds = nfds;
+ select_cb.sem_signalled = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ return -1;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ lwip_link_select_cb(&select_cb);
+
+ /* Increase select_waiting for each socket we are interested in.
+ Also, check for events again: there could have been events between
+ the last scan (without us on the list) and putting us on the list! */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
+
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ waitres = sys_arch_sem_wait_intr(SELECT_SEM_PTR(select_cb.sem), timeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
+
+ /* Decrease select_waiting for each socket we are interested in,
+ and check which events occurred while we waited.
+ It is OK to discard the previous value of nready because
+ we don't set LWIP_POLLSCAN_CLEAR. */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
+
+ lwip_unlink_select_cb(&select_cb);
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT)))
{
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(select_cb.sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_sem_free(&select_cb.sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ /* Unlike lwip_select, lwip_poll never fails with EBADF;
+ it sets revents |= POLLNVAL, instead. */
+
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ goto return_success;
+ }
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_success:
+ set_errno(0);
+ return nready;
+}
+
+/**
+ * Check whether event_callback should wake up a thread waiting in
+ * lwip_poll.
+ */
+static int
+lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, struct
lwip_sock *sock)
+{
+ nfds_t fdi;
+ for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
+ const struct pollfd *pollfd = &scb->poll_fds[fdi];
+ if (pollfd->fd == fd) {
+ /* Do not update pollfd->revents right here;
+ that would be a data race because lwip_pollscan
+ accesses revents without protecting. */
+ if (sock->rcvevent > 0 && (pollfd->events & POLLIN) != 0) {
+ return 1;
+ }
+ if (sock->sendevent != 0 && (pollfd->events & POLLOUT) != 0) {
+ return 1;
+ }
+ if (sock->errevent != 0) {
+ /* POLLERR is output only. */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+#endif /* LWIP_POLL */
+
/**
* Callback registered in the netconn layer for each socket-netconn.
* Processes recvevent (data available) and wakes up tasks waiting for select.
@@ -1747,20 +2016,27 @@ again:
if (scb->sem_signalled == 0) {
/* semaphore not signalled yet */
int do_signal = 0;
- /* Test this select call for our socket */
- if (sock->rcvevent > 0) {
- if (scb->readset && FD_ISSET(s, scb->readset)) {
- do_signal = 1;
+#if LWIP_POLL
+ if (scb->poll_fds != NULL) {
+ do_signal = lwip_poll_should_wake(scb, s, sock);
+ } else
+#endif /* LWIP_POLL */
+ {
+ /* Test this select call for our socket */
+ if (sock->rcvevent > 0) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
}
- }
- if (sock->sendevent != 0) {
- if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
- do_signal = 1;
+ if (sock->sendevent != 0) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
}
- }
- if (sock->errevent != 0) {
- if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
- do_signal = 1;
+ if (sock->errevent != 0) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
}
}
if (do_signal) {
diff --git a/include/lwip/opt.h b/include/lwip/opt.h
index 5c1134c..bdf70c8 100644
--- a/include/lwip/opt.h
+++ b/include/lwip/opt.h
@@ -1753,6 +1753,17 @@
#endif
/**
+ * LWIP_POLL==0: Do not define lwip_poll.
+ * LWIP_POLL==1: Define lwip_poll, using someone else's data types.
+ * LWIP_POLL==2: Define lwip_poll, struct pollfd, nfds_t, and constants.
+ */
+#if defined __DOXYGEN__
+#define LWIP_POLL 1
+#elif !defined LWIP_POLL
+#define LWIP_POLL 0
+#endif
+
+/**
* LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
* Disable this option if you use a POSIX operating system that uses the same
* names (read, write & close). (only used if you use sockets.c)
diff --git a/include/lwip/sockets.h b/include/lwip/sockets.h
index 23a79f0..1b9752c 100644
--- a/include/lwip/sockets.h
+++ b/include/lwip/sockets.h
@@ -270,6 +270,21 @@ typedef struct fd_set
#error "external FD_SETSIZE too small for number of sockets"
#endif /* FD_SET */
+#if LWIP_POLL == 2
+#define POLLIN 1
+#define POLLOUT 2
+#define POLLERR 4
+#define POLLNVAL 8
+/* No support for POLLPRI, POLLHUP, POLLMSG, POLLRDBAND, POLLWRBAND. */
+typedef int nfds_t;
+struct pollfd
+{
+ int fd;
+ short events;
+ short revents;
+};
+#endif
+
/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
* by your system, set this to 0 and include <sys/time.h> in cc.h */
#ifndef LWIP_TIMEVAL_PRIVATE
@@ -306,6 +321,9 @@ void lwip_socket_thread_cleanup(void); /*
LWIP_NETCONN_SEM_PER_THREAD==1: destro
#define lwip_sendto sendto
#define lwip_socket socket
#define lwip_select select
+#if LWIP_POLL
+#define lwip_poll poll
+#endif
#define lwip_ioctlsocket ioctl
#if LWIP_POSIX_SOCKETS_IO_NAMES
@@ -343,6 +361,9 @@ int lwip_write(int s, const void *dataptr, size_t size);
int lwip_writev(int s, const struct iovec *iov, int iovcnt);
int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set
*exceptset,
struct timeval *timeout);
+#if LWIP_POLL
+int lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout);
+#endif
int lwip_ioctl(int s, long cmd, void *argp);
int lwip_fcntl(int s, int cmd, int val);
@@ -382,6 +403,10 @@ int lwip_fcntl(int s, int cmd, int val);
#define socket(domain,type,protocol)
lwip_socket(domain,type,protocol)
/** @ingroup socket */
#define select(maxfdp1,readset,writeset,exceptset,timeout)
lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
+#if LWIP_POLL
+/** @ingroup socket */
+#define poll(fds,nfds,timeout) lwip_poll(fds,nfds,timeout)
+#endif
/** @ingroup socket */
#define ioctlsocket(s,cmd,argp) lwip_ioctl(s,cmd,argp)
Re: [GSoC 2017] Number fo sockets, Kalle Olavi Niemitalo, 2017/08/13
- Re: [GSoC 2017] Number fo sockets, Joan Lledó, 2017/08/13
- Re: [GSoC 2017] Number fo sockets, Kalle Olavi Niemitalo, 2017/08/13
- Re: [GSoC 2017] Number fo sockets, Joan Lledó, 2017/08/15
- Re: [GSoC 2017] Number fo sockets, Joan Lledó, 2017/08/17
- Re: [GSoC 2017] Number fo sockets, Kalle Olavi Niemitalo, 2017/08/17
- Re: [GSoC 2017] Number fo sockets, Joan Lledó, 2017/08/23
- Re: [GSoC 2017] Number fo sockets, Kalle Olavi Niemitalo, 2017/08/23