[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[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: