Use high-resolution timebases for select().
* select.h: Change prototype for select_stuff::wait() for larger microsecond timeouts. * select.cc (pselect): Convert from old cygwin_select(). Implement microsecond timeouts. (cygwin_select): Rewrite as a wrapper on pselect(). (select): Implement microsecond timeouts. (select_stuff::wait): Implement microsecond timeouts with a timer object.
This commit is contained in:
parent
d5632bcfd4
commit
a23e6a35d8
|
@ -85,14 +85,21 @@ details. */
|
||||||
return -1; \
|
return -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static int select (int, fd_set *, fd_set *, fd_set *, DWORD);
|
static int select (int, fd_set *, fd_set *, fd_set *, LONGLONG);
|
||||||
|
|
||||||
/* The main select code. */
|
/* The main select code. */
|
||||||
extern "C" int
|
extern "C" int
|
||||||
cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
pselect (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
struct timeval *to)
|
const struct timespec *to, const sigset_t *set)
|
||||||
{
|
{
|
||||||
select_printf ("select(%d, %p, %p, %p, %p)", maxfds, readfds, writefds, exceptfds, to);
|
sigset_t oldset = _my_tls.sigmask;
|
||||||
|
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
set_signal_mask (_my_tls.sigmask, *set);
|
||||||
|
|
||||||
|
select_printf ("pselect (%d, %p, %p, %p, %p, %p)", maxfds, readfds, writefds, exceptfds, to, set);
|
||||||
|
|
||||||
pthread_testcancel ();
|
pthread_testcancel ();
|
||||||
int res;
|
int res;
|
||||||
|
@ -103,23 +110,43 @@ cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Convert to milliseconds or INFINITE if to == NULL */
|
/* Convert to microseconds or -1 if to == NULL */
|
||||||
DWORD ms = to ? (to->tv_sec * 1000) + (to->tv_usec / 1000) : INFINITE;
|
LONGLONG us = to ? to->tv_sec * 1000000LL + (to->tv_nsec + 999) / 1000 : -1LL;
|
||||||
if (ms == 0 && to->tv_usec)
|
|
||||||
ms = 1; /* At least 1 ms granularity */
|
|
||||||
|
|
||||||
if (to)
|
if (to)
|
||||||
select_printf ("to->tv_sec %ld, to->tv_usec %ld, ms %d", to->tv_sec, to->tv_usec, ms);
|
select_printf ("to->tv_sec %ld, to->tv_nsec %ld, us %D", to->tv_sec, to->tv_nsec, us);
|
||||||
else
|
else
|
||||||
select_printf ("to NULL, ms %x", ms);
|
select_printf ("to NULL, us %D", us);
|
||||||
|
|
||||||
res = select (maxfds, readfds ?: allocfd_set (maxfds),
|
res = select (maxfds, readfds ?: allocfd_set (maxfds),
|
||||||
writefds ?: allocfd_set (maxfds),
|
writefds ?: allocfd_set (maxfds),
|
||||||
exceptfds ?: allocfd_set (maxfds), ms);
|
exceptfds ?: allocfd_set (maxfds), us);
|
||||||
}
|
}
|
||||||
syscall_printf ("%R = select(%d, %p, %p, %p, %p)", res, maxfds, readfds,
|
syscall_printf ("%R = select (%d, %p, %p, %p, %p)", res, maxfds, readfds,
|
||||||
writefds, exceptfds, to);
|
writefds, exceptfds, to);
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
set_signal_mask (_my_tls.sigmask, oldset);
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
|
__except (EFAULT) {}
|
||||||
|
__endtry
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select () is just a wrapper on pselect (). */
|
||||||
|
extern "C" int
|
||||||
|
cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
|
struct timeval *to)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
if (to)
|
||||||
|
{
|
||||||
|
ts.tv_sec = to->tv_sec;
|
||||||
|
ts.tv_nsec = to->tv_usec * 1000;
|
||||||
|
}
|
||||||
|
return pselect (maxfds, readfds, writefds, exceptfds,
|
||||||
|
to ? &ts : NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is arbitrarily split out from cygwin_select to avoid odd
|
/* This function is arbitrarily split out from cygwin_select to avoid odd
|
||||||
|
@ -127,13 +154,13 @@ cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
for the sel variable. */
|
for the sel variable. */
|
||||||
static int
|
static int
|
||||||
select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
DWORD ms)
|
LONGLONG us)
|
||||||
{
|
{
|
||||||
select_stuff::wait_states wait_state = select_stuff::select_loop;
|
select_stuff::wait_states wait_state = select_stuff::select_loop;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Record the current time for later use. */
|
/* Record the current time for later use. */
|
||||||
LONGLONG start_time = gtod.msecs ();
|
LONGLONG start_time = gtod.usecs ();
|
||||||
|
|
||||||
select_stuff sel;
|
select_stuff sel;
|
||||||
sel.return_on_signal = 0;
|
sel.return_on_signal = 0;
|
||||||
|
@ -158,7 +185,7 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
/* Degenerate case. No fds to wait for. Just wait for time to run out
|
/* Degenerate case. No fds to wait for. Just wait for time to run out
|
||||||
or signal to arrive. */
|
or signal to arrive. */
|
||||||
if (sel.start.next == NULL)
|
if (sel.start.next == NULL)
|
||||||
switch (cygwait (ms))
|
switch (cygwait (us * 1000ULL))
|
||||||
{
|
{
|
||||||
case WAIT_SIGNALED:
|
case WAIT_SIGNALED:
|
||||||
select_printf ("signal received");
|
select_printf ("signal received");
|
||||||
|
@ -178,12 +205,12 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
wait_state = select_stuff::select_set_zero;
|
wait_state = select_stuff::select_set_zero;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (sel.always_ready || ms == 0)
|
else if (sel.always_ready || us == 0)
|
||||||
/* Catch any active fds via sel.poll() below */
|
/* Catch any active fds via sel.poll() below */
|
||||||
wait_state = select_stuff::select_ok;
|
wait_state = select_stuff::select_ok;
|
||||||
else
|
else
|
||||||
/* wait for an fd to become active or time out */
|
/* wait for an fd to become active or time out */
|
||||||
wait_state = sel.wait (r, w, e, ms);
|
wait_state = sel.wait (r, w, e, us);
|
||||||
|
|
||||||
select_printf ("sel.wait returns %d", wait_state);
|
select_printf ("sel.wait returns %d", wait_state);
|
||||||
|
|
||||||
|
@ -209,11 +236,11 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
sel.cleanup ();
|
sel.cleanup ();
|
||||||
sel.destroy ();
|
sel.destroy ();
|
||||||
/* Recalculate time remaining to wait if we are going to be looping. */
|
/* Recalculate time remaining to wait if we are going to be looping. */
|
||||||
if (wait_state == select_stuff::select_loop && ms != INFINITE)
|
if (wait_state == select_stuff::select_loop && us != -1)
|
||||||
{
|
{
|
||||||
select_printf ("recalculating ms");
|
select_printf ("recalculating us");
|
||||||
LONGLONG now = gtod.msecs ();
|
LONGLONG now = gtod.usecs ();
|
||||||
if (now > (start_time + ms))
|
if (now > (start_time + us))
|
||||||
{
|
{
|
||||||
select_printf ("timed out after verification");
|
select_printf ("timed out after verification");
|
||||||
/* Set descriptor bits to zero per POSIX. */
|
/* Set descriptor bits to zero per POSIX. */
|
||||||
|
@ -225,9 +252,9 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ms -= (now - start_time);
|
us -= (now - start_time);
|
||||||
start_time = now;
|
start_time = now;
|
||||||
select_printf ("ms now %u", ms);
|
select_printf ("us now %D", us);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,33 +265,6 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int
|
|
||||||
pselect(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
|
||||||
const struct timespec *ts, const sigset_t *set)
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
sigset_t oldset = _my_tls.sigmask;
|
|
||||||
|
|
||||||
__try
|
|
||||||
{
|
|
||||||
if (ts)
|
|
||||||
{
|
|
||||||
tv.tv_sec = ts->tv_sec;
|
|
||||||
tv.tv_usec = ts->tv_nsec / 1000;
|
|
||||||
}
|
|
||||||
if (set)
|
|
||||||
set_signal_mask (_my_tls.sigmask, *set);
|
|
||||||
int ret = cygwin_select (maxfds, readfds, writefds, exceptfds,
|
|
||||||
ts ? &tv : NULL);
|
|
||||||
if (set)
|
|
||||||
set_signal_mask (_my_tls.sigmask, oldset);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
__except (EFAULT) {}
|
|
||||||
__endtry
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call cleanup functions for all inspected fds. Gets rid of any
|
/* Call cleanup functions for all inspected fds. Gets rid of any
|
||||||
executing threads. */
|
executing threads. */
|
||||||
void
|
void
|
||||||
|
@ -362,13 +362,47 @@ err:
|
||||||
/* The heart of select. Waits for an fd to do something interesting. */
|
/* The heart of select. Waits for an fd to do something interesting. */
|
||||||
select_stuff::wait_states
|
select_stuff::wait_states
|
||||||
select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
DWORD ms)
|
LONGLONG us)
|
||||||
{
|
{
|
||||||
HANDLE w4[MAXIMUM_WAIT_OBJECTS];
|
HANDLE w4[MAXIMUM_WAIT_OBJECTS];
|
||||||
select_record *s = &start;
|
select_record *s = &start;
|
||||||
DWORD m = 0;
|
DWORD m = 0;
|
||||||
|
|
||||||
|
/* Always wait for signals. */
|
||||||
wait_signal_arrived here (w4[m++]);
|
wait_signal_arrived here (w4[m++]);
|
||||||
|
|
||||||
|
/* Set a timeout, or not, for WMFO. */
|
||||||
|
DWORD wmfo_timeout = us ? INFINITE : 0;
|
||||||
|
|
||||||
|
/* Create and set a waitable timer, if a finite timeout has been
|
||||||
|
requested. */
|
||||||
|
LARGE_INTEGER ms_clock_ticks;
|
||||||
|
HANDLE timer_handle;
|
||||||
|
NTSTATUS status;
|
||||||
|
select_printf ("before NtCreateTimer\n");
|
||||||
|
status = NtCreateTimer (&timer_handle, TIMER_ALL_ACCESS, NULL, NotificationTimer);
|
||||||
|
select_printf ("after NtCreateTimer\n");
|
||||||
|
if (!NT_SUCCESS (status))
|
||||||
|
{
|
||||||
|
select_printf ("NtCreateTimer failed (%d)\n", GetLastError ());
|
||||||
|
return select_error;
|
||||||
|
}
|
||||||
|
w4[m++] = timer_handle;
|
||||||
|
if (us >= 0)
|
||||||
|
{
|
||||||
|
ms_clock_ticks.QuadPart = -us * 10;
|
||||||
|
int setret;
|
||||||
|
select_printf ("before NtCreateTimer\n");
|
||||||
|
status = NtSetTimer (timer_handle, &ms_clock_ticks, NULL, NULL, FALSE, 0, NULL);
|
||||||
|
select_printf ("after NtCreateTimer\n");
|
||||||
|
if (!NT_SUCCESS (status))
|
||||||
|
{
|
||||||
|
select_printf ("NtSetTimer failed: %d (%08x)\n", setret, GetLastError ());
|
||||||
|
return select_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optionally wait for pthread cancellation. */
|
||||||
if ((w4[m] = pthread::get_cancel_event ()) != NULL)
|
if ((w4[m] = pthread::get_cancel_event ()) != NULL)
|
||||||
m++;
|
m++;
|
||||||
|
|
||||||
|
@ -397,21 +431,30 @@ select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
next_while:;
|
next_while:;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf ("m %d, ms %u", m, ms);
|
debug_printf ("m %d, us %U, wmfo_timeout %d", m, us, wmfo_timeout);
|
||||||
|
|
||||||
DWORD wait_ret;
|
DWORD wait_ret;
|
||||||
if (!windows_used)
|
if (!windows_used)
|
||||||
wait_ret = WaitForMultipleObjects (m, w4, FALSE, ms);
|
wait_ret = WaitForMultipleObjects (m, w4, FALSE, wmfo_timeout);
|
||||||
else
|
else
|
||||||
/* Using MWMO_INPUTAVAILABLE is the officially supported solution for
|
/* Using MWMO_INPUTAVAILABLE is the officially supported solution for
|
||||||
the problem that the call to PeekMessage disarms the queue state
|
the problem that the call to PeekMessage disarms the queue state
|
||||||
so that a subsequent MWFMO hangs, even if there are still messages
|
so that a subsequent MWFMO hangs, even if there are still messages
|
||||||
in the queue. */
|
in the queue. */
|
||||||
wait_ret = MsgWaitForMultipleObjectsEx (m, w4, ms,
|
wait_ret = MsgWaitForMultipleObjectsEx (m, w4, wmfo_timeout,
|
||||||
QS_ALLINPUT | QS_ALLPOSTMESSAGE,
|
QS_ALLINPUT | QS_ALLPOSTMESSAGE,
|
||||||
MWMO_INPUTAVAILABLE);
|
MWMO_INPUTAVAILABLE);
|
||||||
select_printf ("wait_ret %d, m = %d. verifying", wait_ret, m);
|
select_printf ("wait_ret %d, m = %d. verifying", wait_ret, m);
|
||||||
|
|
||||||
|
if (wmfo_timeout == INFINITE)
|
||||||
|
{
|
||||||
|
select_printf ("before timer cleanup\n");
|
||||||
|
BOOLEAN current_state;
|
||||||
|
NtCancelTimer (timer_handle, ¤t_state);
|
||||||
|
NtClose (timer_handle);
|
||||||
|
select_printf ("after timer cleanup\n");
|
||||||
|
}
|
||||||
|
|
||||||
wait_states res;
|
wait_states res;
|
||||||
switch (wait_ret)
|
switch (wait_ret)
|
||||||
{
|
{
|
||||||
|
@ -434,12 +477,13 @@ next_while:;
|
||||||
s->set_select_errno ();
|
s->set_select_errno ();
|
||||||
res = select_error;
|
res = select_error;
|
||||||
break;
|
break;
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
case WAIT_TIMEOUT:
|
case WAIT_TIMEOUT:
|
||||||
select_printf ("timed out");
|
select_printf ("timed out");
|
||||||
res = select_set_zero;
|
res = select_set_zero;
|
||||||
break;
|
break;
|
||||||
case WAIT_OBJECT_0 + 1:
|
case WAIT_OBJECT_0 + 2:
|
||||||
if (startfds > 1)
|
if (startfds > 2)
|
||||||
{
|
{
|
||||||
cleanup ();
|
cleanup ();
|
||||||
destroy ();
|
destroy ();
|
||||||
|
|
|
@ -96,7 +96,7 @@ public:
|
||||||
|
|
||||||
bool test_and_set (int, fd_set *, fd_set *, fd_set *);
|
bool test_and_set (int, fd_set *, fd_set *, fd_set *);
|
||||||
int poll (fd_set *, fd_set *, fd_set *);
|
int poll (fd_set *, fd_set *, fd_set *);
|
||||||
wait_states wait (fd_set *, fd_set *, fd_set *, DWORD);
|
wait_states wait (fd_set *, fd_set *, fd_set *, LONGLONG);
|
||||||
void cleanup ();
|
void cleanup ();
|
||||||
void destroy ();
|
void destroy ();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue