Cygwin: posix timers: implement timer_getoverrun

- set DELAYTIMER_MAX to INT_MAX
- make sure to set siginfo_t::si_overrun, as on Linux

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2019-01-12 21:19:52 +01:00
parent 961be8d726
commit 9e295a8d19
10 changed files with 159 additions and 13 deletions

View File

@ -1484,6 +1484,7 @@ timegm NOSIGFE
timelocal SIGFE timelocal SIGFE
timer_create SIGFE timer_create SIGFE
timer_delete SIGFE timer_delete SIGFE
timer_getoverrun SIGFE
timer_gettime SIGFE timer_gettime SIGFE
timer_settime SIGFE timer_settime SIGFE
times SIGFE times SIGFE

View File

@ -27,6 +27,7 @@ details. */
#include "child_info.h" #include "child_info.h"
#include "ntdll.h" #include "ntdll.h"
#include "exception.h" #include "exception.h"
#include "timer.h"
/* Definitions for code simplification */ /* Definitions for code simplification */
#ifdef __x86_64__ #ifdef __x86_64__
@ -1473,6 +1474,8 @@ sigpacket::process ()
if (handler == SIG_IGN) if (handler == SIG_IGN)
{ {
if (si.si_code == SI_TIMER)
((timer_tracker *) si.si_tid)->disarm_event ();
sigproc_printf ("signal %d ignored", si.si_signo); sigproc_printf ("signal %d ignored", si.si_signo);
goto done; goto done;
} }
@ -1496,6 +1499,8 @@ sigpacket::process ()
|| si.si_signo == SIGCONT || si.si_signo == SIGWINCH || si.si_signo == SIGCONT || si.si_signo == SIGWINCH
|| si.si_signo == SIGURG) || si.si_signo == SIGURG)
{ {
if (si.si_code == SI_TIMER)
((timer_tracker *) si.si_tid)->disarm_event ();
sigproc_printf ("signal %d default is currently ignore", si.si_signo); sigproc_printf ("signal %d default is currently ignore", si.si_signo);
goto done; goto done;
} }
@ -1620,6 +1625,13 @@ _cygtls::call_signal_handler ()
sigset_t this_oldmask = set_process_mask_delta (); sigset_t this_oldmask = set_process_mask_delta ();
if (infodata.si_code == SI_TIMER)
{
timer_tracker *tt = (timer_tracker *)
infodata.si_tid;
infodata.si_overrun = tt->disarm_event ();
}
/* Save information locally on stack to pass to handler. */ /* Save information locally on stack to pass to handler. */
int thissig = sig; int thissig = sig;
siginfo_t thissi = infodata; siginfo_t thissi = infodata;

View File

@ -501,12 +501,13 @@ details. */
329: Export sched_getcpu. 329: Export sched_getcpu.
330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE, 330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE,
CLOCK_BOOTTIME. CLOCK_BOOTTIME.
331: Add timer_getoverrun, DELAYTIMER_MAX.
Note that we forgot to bump the api for ualarm, strtoll, strtoull, Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */ sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0 #define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 330 #define CYGWIN_VERSION_API_MINOR 331
/* There is also a compatibity version number associated with the shared memory /* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared regions. It is incremented when incompatible changes are made to the shared

View File

@ -184,7 +184,7 @@ details. */
/* Maximum number of timer expiration overruns. Not yet implemented. */ /* Maximum number of timer expiration overruns. Not yet implemented. */
#undef DELAYTIMER_MAX #undef DELAYTIMER_MAX
/* #define DELAYTIMER_MAX >= _POSIX_DELAYTIMER_MAX */ #define DELAYTIMER_MAX __INT_MAX__
/* Maximum length of a host name. */ /* Maximum length of a host name. */
#undef HOST_NAME_MAX #undef HOST_NAME_MAX

View File

@ -24,6 +24,11 @@ What's new:
- Support Linux-specific linkat(2) flag AT_EMPTY_PATH. - Support Linux-specific linkat(2) flag AT_EMPTY_PATH.
- Support overrun counter for posix timers (via timer_getoverrun() or
siginfo_t::si_overrun).
- New API: timer_getoverrun.
What changed: What changed:
------------- -------------

View File

@ -20,6 +20,7 @@ details. */
#include "dtable.h" #include "dtable.h"
#include "cygheap.h" #include "cygheap.h"
#include "cygwait.h" #include "cygwait.h"
#include "timer.h"
#define _SA_NORESTART 0x8000 #define _SA_NORESTART 0x8000
@ -611,6 +612,12 @@ sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
else else
{ {
_my_tls.lock (); _my_tls.lock ();
if (_my_tls.infodata.si_code == SI_TIMER)
{
timer_tracker *tt = (timer_tracker *)
_my_tls.infodata.si_tid;
_my_tls.infodata.si_overrun = tt->disarm_event ();
}
if (info) if (info)
*info = _my_tls.infodata; *info = _my_tls.infodata;
res = _my_tls.infodata.si_signo; res = _my_tls.infodata.si_signo;

View File

@ -17,6 +17,10 @@ details. */
#include "timer.h" #include "timer.h"
#include <sys/param.h> #include <sys/param.h>
#define EVENT_DISARMED 0
#define EVENT_ARMED -1
#define EVENT_LOCK 1
timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL); timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL);
class lock_timer_tracker class lock_timer_tracker
@ -79,6 +83,9 @@ timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
clock_id = c; clock_id = c;
magic = TT_MAGIC; magic = TT_MAGIC;
hcancel = NULL; hcancel = NULL;
event_running = EVENT_DISARMED;
overrun_count_curr = 0;
overrun_count = 0;
if (this != &ttstart) if (this != &ttstart)
{ {
lock_timer_tracker here; lock_timer_tracker here;
@ -96,6 +103,57 @@ timespec_to_us (const timespec& ts)
return res; return res;
} }
/* Returns 0 if arming successful, -1 if a signal is already queued.
If so, it also increments overrun_count. */
LONG
timer_tracker::arm_event ()
{
LONG ret;
while ((ret = InterlockedCompareExchange (&event_running, EVENT_ARMED,
EVENT_DISARMED)) == EVENT_LOCK)
yield ();
if (ret == EVENT_ARMED)
InterlockedIncrement64 (&overrun_count);
return ret;
}
LONG
timer_tracker::disarm_event ()
{
LONG ret;
while ((ret = InterlockedCompareExchange (&event_running, EVENT_LOCK,
EVENT_ARMED)) == EVENT_LOCK)
yield ();
if (ret == EVENT_ARMED)
{
LONG64 ov_cnt;
InterlockedExchange64 (&ov_cnt, overrun_count);
if (ov_cnt > DELAYTIMER_MAX || ov_cnt < 0)
overrun_count_curr = DELAYTIMER_MAX;
else
overrun_count_curr = ov_cnt;
ret = overrun_count_curr;
InterlockedExchange64 (&overrun_count, 0);
InterlockedExchange (&event_running, EVENT_DISARMED);
}
return ret;
}
static void *
notify_thread_wrapper (void *arg)
{
timer_tracker *tt = (timer_tracker *) arg;
sigevent_t *evt = tt->sigevt ();
void * (*notify_func) (void *) = (void * (*) (void *))
evt->sigev_notify_function;
tt->disarm_event ();
return notify_func (evt->sigev_value.sival_ptr);
}
DWORD DWORD
timer_tracker::thread_func () timer_tracker::thread_func ()
{ {
@ -117,7 +175,10 @@ timer_tracker::thread_func ()
} }
else else
{ {
sleepto_us = now; int64_t num_intervals = (now - cur_sleepto_us) / interval_us;
InterlockedAdd64 (&overrun_count, num_intervals);
cur_sleepto_us += num_intervals * interval_us;
sleepto_us = cur_sleepto_us;
sleep_ms = 0; sleep_ms = 0;
} }
@ -139,16 +200,27 @@ timer_tracker::thread_func ()
{ {
case SIGEV_SIGNAL: case SIGEV_SIGNAL:
{ {
if (arm_event ())
{
debug_printf ("%p timer signal already queued", this);
break;
}
siginfo_t si = {0}; siginfo_t si = {0};
si.si_signo = evp.sigev_signo; si.si_signo = evp.sigev_signo;
si.si_sigval.sival_ptr = evp.sigev_value.sival_ptr;
si.si_code = SI_TIMER; si.si_code = SI_TIMER;
si.si_tid = (timer_t) this;
si.si_sigval.sival_ptr = evp.sigev_value.sival_ptr;
debug_printf ("%p sending signal %d", this, evp.sigev_signo); debug_printf ("%p sending signal %d", this, evp.sigev_signo);
sig_send (myself_nowait, si); sig_send (myself_nowait, si);
break; break;
} }
case SIGEV_THREAD: case SIGEV_THREAD:
{ {
if (arm_event ())
{
debug_printf ("%p timer thread already queued", this);
break;
}
pthread_t notify_thread; pthread_t notify_thread;
debug_printf ("%p starting thread", this); debug_printf ("%p starting thread", this);
pthread_attr_t *attr; pthread_attr_t *attr;
@ -160,16 +232,13 @@ timer_tracker::thread_func ()
pthread_attr_init(attr = &default_attr); pthread_attr_init(attr = &default_attr);
pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED);
} }
int rc = pthread_create (&notify_thread, attr, int rc = pthread_create (&notify_thread, attr,
(void * (*) (void *)) evp.sigev_notify_function, notify_thread_wrapper, this);
evp.sigev_value.sival_ptr);
if (rc) if (rc)
{ {
debug_printf ("thread creation failed, %E"); debug_printf ("thread creation failed, %E");
return 0; return 0;
} }
// FIXME: pthread_join?
break; break;
} }
} }
@ -219,9 +288,6 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu
if (timespec_bad (value->it_value) || timespec_bad (value->it_interval)) if (timespec_bad (value->it_value) || timespec_bad (value->it_interval))
__leave; __leave;
long long now = in_flags & TIMER_ABSTIME ?
0 : get_clock (clock_id)->usecs ();
lock_timer_tracker here; lock_timer_tracker here;
cancel (); cancel ();
@ -232,8 +298,23 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu
interval_us = sleepto_us = 0; interval_us = sleepto_us = 0;
else else
{ {
sleepto_us = now + timespec_to_us (value->it_value);
interval_us = timespec_to_us (value->it_interval); interval_us = timespec_to_us (value->it_interval);
if (in_flags & TIMER_ABSTIME)
{
int64_t now = get_clock (clock_id)->usecs ();
sleepto_us = timespec_to_us (value->it_value);
if (sleepto_us <= now)
{
int64_t ov_cnt = (now - sleepto_us + (interval_us + 1))
/ interval_us;
InterlockedAdd64 (&overrun_count, ov_cnt);
sleepto_us += ov_cnt * interval_us;
}
}
else
sleepto_us = get_clock (clock_id)->usecs ()
+ timespec_to_us (value->it_value);
it_interval = value->it_interval; it_interval = value->it_interval;
if (!hcancel) if (!hcancel)
hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
@ -285,6 +366,9 @@ void
timer_tracker::fixup_after_fork () timer_tracker::fixup_after_fork ()
{ {
ttstart.hcancel = ttstart.syncthread = NULL; ttstart.hcancel = ttstart.syncthread = NULL;
ttstart.event_running = EVENT_DISARMED;
ttstart.overrun_count_curr = 0;
ttstart.overrun_count = 0;
for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */) for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */)
{ {
timer_tracker *deleteme = tt->next; timer_tracker *deleteme = tt->next;
@ -372,6 +456,26 @@ timer_settime (timer_t timerid, int flags,
return ret; return ret;
} }
extern "C" int
timer_getoverrun (timer_t timerid)
{
int ret = -1;
__try
{
timer_tracker *tt = (timer_tracker *) timerid;
if (!tt->is_timer_tracker ())
{
set_errno (EINVAL);
__leave;
}
ret = tt->getoverrun ();
}
__except (EFAULT) {}
__endtry
return ret;
}
extern "C" int extern "C" int
timer_delete (timer_t timerid) timer_delete (timer_t timerid)
{ {

View File

@ -22,6 +22,9 @@ class timer_tracker
HANDLE syncthread; HANDLE syncthread;
int64_t interval_us; int64_t interval_us;
int64_t sleepto_us; int64_t sleepto_us;
LONG event_running;
LONG overrun_count_curr;
LONG64 overrun_count;
bool cancel (); bool cancel ();
@ -29,10 +32,14 @@ class timer_tracker
timer_tracker (clockid_t, const sigevent *); timer_tracker (clockid_t, const sigevent *);
~timer_tracker (); ~timer_tracker ();
inline bool is_timer_tracker () const { return magic == TT_MAGIC; } inline bool is_timer_tracker () const { return magic == TT_MAGIC; }
inline sigevent_t *sigevt () { return &evp; }
inline int getoverrun () const { return overrun_count_curr; }
void gettime (itimerspec *); void gettime (itimerspec *);
int settime (int, const itimerspec *, itimerspec *); int settime (int, const itimerspec *, itimerspec *);
int clean_and_unhook (); int clean_and_unhook ();
LONG arm_event ();
LONG disarm_event ();
DWORD thread_func (); DWORD thread_func ();
static void fixup_after_fork (); static void fixup_after_fork ();

View File

@ -45,6 +45,15 @@ Support Linux-specific open(2) flag O_PATH.
- Support Linux-specific linkat(2) flag AT_EMPTY_PATH. - Support Linux-specific linkat(2) flag AT_EMPTY_PATH.
</para></listitem> </para></listitem>
<listitem><para>
Support overrun counter for posix timers (via timer_getoverrun() or
siginfo_t::si_overrun).
</para></listitem>
<listitem><para>
New API: timer_getoverrun.
</para></listitem>
<listitem><para> <listitem><para>
clock_nanosleep, pthread_condattr_setclock and timer_create now support clock_nanosleep, pthread_condattr_setclock and timer_create now support
all clocks, except CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. all clocks, except CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID.

View File

@ -1003,6 +1003,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
time time
timer_create (see chapter "Implementation Notes") timer_create (see chapter "Implementation Notes")
timer_delete timer_delete
timer_getoverrun
timer_gettime timer_gettime
timer_settime timer_settime
times times
@ -1587,7 +1588,6 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
pthread_mutex_consistent pthread_mutex_consistent
putmsg putmsg
setnetent setnetent
timer_getoverrun
ulimit ulimit
waitid waitid
</screen> </screen>