diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 153e3847f..a446e754e 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -523,6 +523,13 @@ class fhandler_socket: public fhandler_base void rmem (int nrmem) { _rmem = nrmem; } 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: diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 6eac689af..92b4db9f5 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 6f96acbd2..43da5dcac 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 000000000..f8fd4cb42 --- /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 86e32b8bf..198fc32ce 100644 --- a/winsup/cygwin/times.cc +++ b/winsup/cygwin/times.cc @@ -215,6 +215,22 @@ timeval_to_filetime (const struct timeval *time_in, PLARGE_INTEGER out) + time_in->tv_usec * (NS100PERSEC/USPERSEC) + FACTOR; } +/* 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 fcb08e213..1b3fbfeee 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 *);