diff options
-rw-r--r-- | winsup/cygwin/cygwait.h | 27 | ||||
-rw-r--r-- | winsup/cygwin/select.cc | 63 | ||||
-rw-r--r-- | winsup/cygwin/select.h | 1 |
3 files changed, 76 insertions, 15 deletions
diff --git a/winsup/cygwin/cygwait.h b/winsup/cygwin/cygwait.h index 1240f5404..3e02cdd66 100644 --- a/winsup/cygwin/cygwait.h +++ b/winsup/cygwin/cygwait.h @@ -59,3 +59,30 @@ cygwait (DWORD howlong) { return cygwait (NULL, howlong); } + +extern inline DWORD __attribute__ ((always_inline)) +cygwait_us (HANDLE h, LONGLONG howlong, unsigned mask) +{ + LARGE_INTEGER li_howlong; + PLARGE_INTEGER pli_howlong; + if (howlong < 0LL) + pli_howlong = NULL; + else + { + li_howlong.QuadPart = -(10LL * howlong); + pli_howlong = &li_howlong; + } + return cygwait (h, pli_howlong, mask); +} + +static inline DWORD __attribute__ ((always_inline)) +cygwait_us (HANDLE h, LONGLONG howlong = -1) +{ + return cygwait_us (h, howlong, cw_cancel | cw_sig); +} + +static inline DWORD __attribute__ ((always_inline)) +cygwait_us (LONGLONG howlong) +{ + return cygwait_us (NULL, howlong); +} diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 556a79f21..25b0c5a98 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -32,6 +32,7 @@ details. */ #include "pinfo.h" #include "sigproc.h" #include "cygtls.h" +#include "cygwait.h" /* * All these defines below should be in sys/types.h @@ -155,7 +156,7 @@ static int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, LONGLONG us) { - select_stuff::wait_states wait_state = select_stuff::select_set_zero; + select_stuff::wait_states wait_state = select_stuff::select_loop; int ret = 0; /* Record the current time for later use. */ @@ -181,7 +182,30 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, } select_printf ("sel.always_ready %d", sel.always_ready); - if (sel.always_ready || us == 0) + /* Degenerate case. No fds to wait for. Just wait for time to run out + or signal to arrive. */ + if (sel.start.next == NULL) + switch (cygwait_us (us)) + { + case WAIT_SIGNALED: + select_printf ("signal received"); + /* select() is always interrupted by a signal so set EINTR, + unconditionally, ignoring any SA_RESTART detection by + call_signal_handler(). */ + _my_tls.call_signal_handler (); + set_sig_errno (EINTR); + wait_state = select_stuff::select_signalled; + break; + case WAIT_CANCELED: + sel.destroy (); + pthread::static_cancel_self (); + /*NOTREACHED*/ + default: + /* Set wait_state to zero below. */ + wait_state = select_stuff::select_set_zero; + break; + } + else if (sel.always_ready || us == 0) /* Catch any active fds via sel.poll() below */ wait_state = select_stuff::select_ok; else @@ -190,24 +214,29 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, select_printf ("sel.wait returns %d", wait_state); - if (wait_state == select_stuff::select_ok) + if (wait_state >= select_stuff::select_ok) { UNIX_FD_ZERO (readfds, maxfds); UNIX_FD_ZERO (writefds, maxfds); UNIX_FD_ZERO (exceptfds, maxfds); - /* Set bit mask from sel records. This also sets ret to the - right value >= 0, matching the number of bits set in the - fds records. if ret is 0, continue to loop. */ - ret = sel.poll (readfds, writefds, exceptfds); - if (!ret) - wait_state = select_stuff::select_set_zero; + if (wait_state == select_stuff::select_set_zero) + ret = 0; + else + { + /* Set bit mask from sel records. This also sets ret to the + right value >= 0, matching the number of bits set in the + fds records. if ret is 0, continue to loop. */ + ret = sel.poll (readfds, writefds, exceptfds); + if (!ret) + wait_state = select_stuff::select_loop; + } } /* Always clean up everything here. If we're looping then build it all up again. */ sel.cleanup (); sel.destroy (); - /* Check and recalculate timeout. */ - if (us != -1LL && wait_state == select_stuff::select_set_zero) + /* Recalculate time remaining to wait if we are going to be looping. */ + if (wait_state == select_stuff::select_loop && us != -1) { select_printf ("recalculating us"); LONGLONG now = gtod.usecs (); @@ -229,7 +258,7 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, } } } - while (wait_state == select_stuff::select_set_zero); + while (wait_state == select_stuff::select_loop); if (wait_state < select_stuff::select_ok) ret = -1; @@ -465,7 +494,7 @@ next_while:; to wait for. */ default: s = &start; - res = select_set_zero; + bool gotone = false; /* Some types of objects (e.g., consoles) wake up on "inappropriate" events like mouse movements. The verify function will detect these situations. If it returns false, then this wakeup was a false alarm @@ -479,9 +508,13 @@ next_while:; } else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) && s->verify (s, readfds, writefds, exceptfds)) - res = select_ok; + gotone = true; - select_printf ("res after verify %d", res); + if (!gotone) + res = select_loop; + else + res = select_ok; + select_printf ("gotone %d", gotone); break; } out: diff --git a/winsup/cygwin/select.h b/winsup/cygwin/select.h index 3c749ad6e..581ee4e73 100644 --- a/winsup/cygwin/select.h +++ b/winsup/cygwin/select.h @@ -78,6 +78,7 @@ public: enum wait_states { select_signalled = -3, + select_loop = -2, select_error = -1, select_ok = 0, select_set_zero = 1 |