https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=c51a0b74dcec39222d5a61189bfe4a6aa73dcf46

commit c51a0b74dcec39222d5a61189bfe4a6aa73dcf46
Author: Corinna Vinschen <[email protected]>
Date:   Wed Feb 7 16:16:51 2018 +0100

    Cygwin: sockets: Handle SO_RCVTIMEO and SO_SNDTIMEO
    
    Signed-off-by: Corinna Vinschen <[email protected]>

Diff:
---
 winsup/cygwin/fhandler.h         |  7 +++++++
 winsup/cygwin/fhandler_socket.cc | 21 ++++++++++++++++++--
 winsup/cygwin/net.cc             | 42 ++++++++++++++++++++++++++++++++++++++++
 winsup/cygwin/release/2.10.1     | 13 +++++++++++++
 winsup/cygwin/times.cc           | 16 +++++++++++++++
 winsup/cygwin/winsup.h           |  1 +
 6 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 153e384..a446e75 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -524,6 +524,13 @@ class fhandler_socket: public fhandler_base
   void wmem (int nwmem) { _wmem = nwmem; }
 
  private:
+  DWORD _rcvtimeo; /* msecs */
+  DWORD _sndtimeo; /* msecs */
+ public:
+  DWORD &rcvtimeo () { return _rcvtimeo; }
+  DWORD &sndtimeo () { return _sndtimeo; }
+
+ private:
   struct _WSAPROTOCOL_INFOW *prot_info_ptr;
  public:
   void init_fixup_before ();
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index 6eac689..92b4db9 100644
--- a/winsup/cygwin/fhandler_socket.cc
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -227,6 +227,8 @@ fhandler_socket::fhandler_socket () :
   wsock_events (NULL),
   wsock_mtx (NULL),
   wsock_evt (NULL),
+  _rcvtimeo (INFINITE),
+  _sndtimeo (INFINITE),
   prot_info_ptr (NULL),
   sun_path (NULL),
   peer_sun_path (NULL),
@@ -752,6 +754,8 @@ fhandler_socket::wait_for_events (const long event_mask, 
const DWORD flags)
 
   int ret;
   long events = 0;
+  DWORD wfmo_timeout = 50;
+  DWORD timeout;
 
   WSAEVENT ev[3] = { wsock_evt, NULL, NULL };
   wait_signal_arrived here (ev[1]);
@@ -759,19 +763,32 @@ fhandler_socket::wait_for_events (const long event_mask, 
const DWORD flags)
   if ((ev[2] = pthread::get_cancel_event ()) != NULL)
     ++ev_cnt;
 
+  if (is_nonblocking () || (flags & MSG_DONTWAIT))
+    timeout = 0;
+  else if (event_mask & FD_READ)
+    timeout = rcvtimeo ();
+  else if (event_mask & FD_WRITE)
+    timeout = sndtimeo ();
+  else
+    timeout = INFINITE;
+
   while (!(ret = evaluate_events (event_mask, events, !(flags & MSG_PEEK)))
         && !events)
     {
-      if (is_nonblocking () || (flags & MSG_DONTWAIT))
+      if (timeout == 0)
        {
          WSASetLastError (WSAEWOULDBLOCK);
          return SOCKET_ERROR;
        }
 
-      switch (WSAWaitForMultipleEvents (ev_cnt, ev, FALSE, 50, FALSE))
+      if (timeout < wfmo_timeout)
+       wfmo_timeout = timeout;
+      switch (WSAWaitForMultipleEvents (ev_cnt, ev, FALSE, wfmo_timeout, 
FALSE))
        {
          case WSA_WAIT_TIMEOUT:
          case WSA_WAIT_EVENT_0:
+           if (timeout != INFINITE)
+             timeout -= wfmo_timeout;
            break;
 
          case WSA_WAIT_EVENT_0 + 1:
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc
index 6f96acb..43da5dc 100644
--- a/winsup/cygwin/net.cc
+++ b/winsup/cygwin/net.cc
@@ -851,6 +851,21 @@ cygwin_setsockopt (int fd, int level, int optname, const 
void *optval,
                ignore = true;
              break;
 
+           case SO_RCVTIMEO:
+           case SO_SNDTIMEO:
+             if (optlen < (socklen_t) sizeof (struct timeval))
+               {
+                 set_errno (EINVAL);
+                 __leave;
+               }
+             if (timeval_to_ms ((struct timeval *) optval,
+                                (optname == SO_RCVTIMEO)
+                                ? fh->rcvtimeo () : fh->sndtimeo ()))
+               res = 0;
+             else
+               set_errno (EDOM);
+             __leave;
+
            default:
              break;
            }
@@ -999,6 +1014,33 @@ cygwin_getsockopt (int fd, int level, int optname, void 
*optval,
              }
              break;
 
+           case SO_RCVTIMEO:
+           case SO_SNDTIMEO:
+             {
+               struct timeval *time_out = (struct timeval *) optval;
+
+               if (*optlen < (socklen_t) sizeof *time_out)
+                 {
+                   set_errno (EINVAL);
+                   __leave;
+                 }
+               DWORD ms = (optname == SO_RCVTIMEO) ? fh->rcvtimeo ()
+                                                   : fh->sndtimeo ();
+               if (ms == 0 || ms == INFINITE)
+                 {
+                   time_out->tv_sec = 0;
+                   time_out->tv_usec = 0;
+                 }
+               else
+                 {
+                   time_out->tv_sec = ms / HZ;
+                   time_out->tv_usec = ((ms % HZ) * USPERSEC) / HZ;
+                 }
+               *optlen = (socklen_t) sizeof *time_out;
+               res = 0;
+               __leave;
+             }
+
            default:
              break;
            }
diff --git a/winsup/cygwin/release/2.10.1 b/winsup/cygwin/release/2.10.1
new file mode 100644
index 0000000..f8fd4cb
--- /dev/null
+++ b/winsup/cygwin/release/2.10.1
@@ -0,0 +1,13 @@
+What's new:
+-----------
+
+
+What changed:
+-------------
+
+- SO_RCVTIMEO and SO_SNDTIMEO socket options are now honored.
+
+
+Bug Fixes
+---------
+
diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc
index 86e32b8..198fc32 100644
--- a/winsup/cygwin/times.cc
+++ b/winsup/cygwin/times.cc
@@ -216,6 +216,22 @@ timeval_to_filetime (const struct timeval *time_in, 
PLARGE_INTEGER out)
 }
 
 /* Cygwin internal */
+bool
+timeval_to_ms (const struct timeval *time_in, DWORD &ms)
+{
+  if (time_in->tv_sec < 0 || time_in->tv_usec < 0
+      || time_in->tv_usec >= USPERSEC)
+    return false;
+  if ((time_in->tv_sec == 0 && time_in->tv_usec == 0)
+      || time_in->tv_sec >= INFINITE / HZ)
+    ms = INFINITE;
+  else
+    ms = time_in->tv_sec * HZ + (time_in->tv_usec + (USPERSEC/HZ) - 1)
+                               / (USPERSEC/HZ);
+  return true;
+}
+
+/* Cygwin internal */
 static timeval __stdcall
 time_t_to_timeval (time_t in)
 {
diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h
index fcb08e2..1b3fbfe 100644
--- a/winsup/cygwin/winsup.h
+++ b/winsup/cygwin/winsup.h
@@ -206,6 +206,7 @@ void __stdcall to_timestruc_t (PLARGE_INTEGER, timestruc_t 
*);
 void __stdcall time_as_timestruc_t (timestruc_t *);
 void __stdcall timeval_to_filetime (const struct timeval *, PLARGE_INTEGER);
 void __stdcall timespec_to_filetime (const struct timespec *, PLARGE_INTEGER);
+bool timeval_to_ms (const struct timeval *, DWORD &);
 
 /* Console related */
 void __stdcall set_console_title (char *);

Reply via email to