[PATCH 5/7] POLL: New patch implementing poll()
---
debian/patches/poll | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 518 insertions(+)
create mode 100644 debian/patches/poll
diff --git a/debian/patches/poll b/debian/patches/poll
new file mode 100644
index 0000000..b85ae06
--- /dev/null
+++ b/debian/patches/poll
@@ -0,0 +1,518 @@
+--- a/src/api/sockets.c
++++ b/src/api/sockets.c
+@@ -238,6 +238,12 @@
+ 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 */
+@@ -1288,6 +1294,51 @@
+ 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
+@@ -1411,6 +1462,10 @@
+ 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();
+@@ -1422,20 +1477,7 @@
+ }
+ #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;
+@@ -1505,21 +1547,7 @@
+ 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))) {
+@@ -1563,6 +1591,253 @@
+ 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;
++ u32_t msectimeout;
++ struct lwip_select_cb select_cb;
++#if LWIP_NETCONN_SEM_PER_THREAD
++ int waited = 0;
++#endif
++ 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 */
++ if (timeout < 0) {
++ /* Wait forever */
++ msectimeout = 0;
++ } else {
++ /* timeout == 0 would have been handled earlier. */
++ LWIP_ASSERT("timeout > 0", timeout > 0);
++ msectimeout = timeout;
++ }
++ waitres = sys_arch_sem_wait_intr(SELECT_SEM_PTR(select_cb.sem), msectimeout);
++#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.
+@@ -1649,20 +1924,27 @@
+ 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) {
+--- a/src/include/lwip/opt.h
++++ b/src/include/lwip/opt.h
+@@ -1751,6 +1751,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)
+--- a/src/include/lwip/lwip_sockets.h
++++ b/src/include/lwip/lwip_sockets.h
+@@ -430,6 +430,21 @@
+ #error "external FD_SETSIZE too small for number of sockets"
+ #endif /* FD_SET */
+
++#if LWIP_POLL == 2
++#define POLLIN 0x01
++#define POLLOUT 0x02
++#define POLLERR 0x04
++#define POLLNVAL 0x08
++/* 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
+@@ -466,6 +481,9 @@
+ #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
+@@ -516,6 +534,10 @@
+ #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)
+
+--- a/src/include/lwip/sockets.h
++++ b/src/include/lwip/sockets.h
+@@ -75,6 +75,9 @@
+ 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);
+
+--- a/port/include/lwipopts.h
++++ b/port/include/lwipopts.h
+@@ -26,6 +26,9 @@
+ /* Don't rename Sockets API functions */
+ #define LWIP_COMPAT_SOCKETS 0
+
++/* We're using lwip_poll() */
++#define LWIP_POLL 1
++
+ /* Use Glibc's sockets headers */
+ #define LWIP_STD_SOCKETS 1
+ #define LWIP_INCLUDE_STD_SOCKETS "posix/socket.h"
+--- a/port/include/arch/cc.h
++++ b/port/include/arch/cc.h
+@@ -46,6 +46,9 @@
+ #include <netinet/tcp.h>
+ #include <netinet/udp.h>
+
++/* We use poll() instead of select()*/
++#include <poll.h>
++
+ /* Use our own htons() and pals */
+ #define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 1
+
--
2.14.0
Reply to: