From: Corinna Vinschen Date: Sat, 12 Jan 2019 20:19:52 +0000 (+0100) Subject: Cygwin: posix timers: implement timer_getoverrun X-Git-Tag: cygwin-3_0_0-release~131 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=9e295a8d193e138808931816f5858761ff08f402;p=newlib-cygwin.git 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 --- diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index d1d955542..a6363e1ba 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -1484,6 +1484,7 @@ timegm NOSIGFE timelocal SIGFE timer_create SIGFE timer_delete SIGFE +timer_getoverrun SIGFE timer_gettime SIGFE timer_settime SIGFE times SIGFE diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 419b0976f..8c1b3b4e6 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -27,6 +27,7 @@ details. */ #include "child_info.h" #include "ntdll.h" #include "exception.h" +#include "timer.h" /* Definitions for code simplification */ #ifdef __x86_64__ @@ -1473,6 +1474,8 @@ sigpacket::process () 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); goto done; } @@ -1496,6 +1499,8 @@ sigpacket::process () || si.si_signo == SIGCONT || si.si_signo == SIGWINCH || 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); goto done; } @@ -1620,6 +1625,13 @@ _cygtls::call_signal_handler () 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. */ int thissig = sig; siginfo_t thissi = infodata; diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index bb1fa9746..1bcec331f 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -501,12 +501,13 @@ details. */ 329: Export sched_getcpu. 330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE, CLOCK_BOOTTIME. + 331: Add timer_getoverrun, DELAYTIMER_MAX. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #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 regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/include/limits.h b/winsup/cygwin/include/limits.h index 3550c4fcb..524a469e4 100644 --- a/winsup/cygwin/include/limits.h +++ b/winsup/cygwin/include/limits.h @@ -184,7 +184,7 @@ details. */ /* Maximum number of timer expiration overruns. Not yet implemented. */ #undef DELAYTIMER_MAX -/* #define DELAYTIMER_MAX >= _POSIX_DELAYTIMER_MAX */ +#define DELAYTIMER_MAX __INT_MAX__ /* Maximum length of a host name. */ #undef HOST_NAME_MAX diff --git a/winsup/cygwin/release/2.12.0 b/winsup/cygwin/release/2.12.0 index b77278238..15f07e021 100644 --- a/winsup/cygwin/release/2.12.0 +++ b/winsup/cygwin/release/2.12.0 @@ -24,6 +24,11 @@ What's new: - 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: ------------- diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index b3e257b23..74d6eb675 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -20,6 +20,7 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "cygwait.h" +#include "timer.h" #define _SA_NORESTART 0x8000 @@ -611,6 +612,12 @@ sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime) else { _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) *info = _my_tls.infodata; res = _my_tls.infodata.si_signo; diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index 0aeba5830..e38decb9a 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -17,6 +17,10 @@ details. */ #include "timer.h" #include +#define EVENT_DISARMED 0 +#define EVENT_ARMED -1 +#define EVENT_LOCK 1 + timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL); class lock_timer_tracker @@ -79,6 +83,9 @@ timer_tracker::timer_tracker (clockid_t c, const sigevent *e) clock_id = c; magic = TT_MAGIC; hcancel = NULL; + event_running = EVENT_DISARMED; + overrun_count_curr = 0; + overrun_count = 0; if (this != &ttstart) { lock_timer_tracker here; @@ -96,6 +103,57 @@ timespec_to_us (const timespec& ts) 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 timer_tracker::thread_func () { @@ -117,7 +175,10 @@ timer_tracker::thread_func () } 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; } @@ -139,16 +200,27 @@ timer_tracker::thread_func () { case SIGEV_SIGNAL: { + if (arm_event ()) + { + debug_printf ("%p timer signal already queued", this); + break; + } siginfo_t si = {0}; si.si_signo = evp.sigev_signo; - si.si_sigval.sival_ptr = evp.sigev_value.sival_ptr; 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); sig_send (myself_nowait, si); break; } case SIGEV_THREAD: { + if (arm_event ()) + { + debug_printf ("%p timer thread already queued", this); + break; + } pthread_t notify_thread; debug_printf ("%p starting thread", this); pthread_attr_t *attr; @@ -160,16 +232,13 @@ timer_tracker::thread_func () pthread_attr_init(attr = &default_attr); pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED); } - int rc = pthread_create (¬ify_thread, attr, - (void * (*) (void *)) evp.sigev_notify_function, - evp.sigev_value.sival_ptr); + notify_thread_wrapper, this); if (rc) { debug_printf ("thread creation failed, %E"); return 0; } - // FIXME: pthread_join? 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)) __leave; - long long now = in_flags & TIMER_ABSTIME ? - 0 : get_clock (clock_id)->usecs (); - lock_timer_tracker here; cancel (); @@ -232,8 +298,23 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu interval_us = sleepto_us = 0; else { - sleepto_us = now + timespec_to_us (value->it_value); 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; if (!hcancel) hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); @@ -285,6 +366,9 @@ void timer_tracker::fixup_after_fork () { 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 */) { timer_tracker *deleteme = tt->next; @@ -372,6 +456,26 @@ timer_settime (timer_t timerid, int flags, 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 timer_delete (timer_t timerid) { diff --git a/winsup/cygwin/timer.h b/winsup/cygwin/timer.h index 0442c37d1..f75cd487c 100644 --- a/winsup/cygwin/timer.h +++ b/winsup/cygwin/timer.h @@ -22,6 +22,9 @@ class timer_tracker HANDLE syncthread; int64_t interval_us; int64_t sleepto_us; + LONG event_running; + LONG overrun_count_curr; + LONG64 overrun_count; bool cancel (); @@ -29,10 +32,14 @@ class timer_tracker timer_tracker (clockid_t, const sigevent *); ~timer_tracker (); 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 *); int settime (int, const itimerspec *, itimerspec *); int clean_and_unhook (); + LONG arm_event (); + LONG disarm_event (); DWORD thread_func (); static void fixup_after_fork (); diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index 6af7270f9..2e1645fbe 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -45,6 +45,15 @@ Support Linux-specific open(2) flag O_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. + + clock_nanosleep, pthread_condattr_setclock and timer_create now support all clocks, except CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml index afa1ca409..021b5962c 100644 --- a/winsup/doc/posix.xml +++ b/winsup/doc/posix.xml @@ -1003,6 +1003,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). time timer_create (see chapter "Implementation Notes") timer_delete + timer_getoverrun timer_gettime timer_settime times @@ -1587,7 +1588,6 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). pthread_mutex_consistent putmsg setnetent - timer_getoverrun ulimit waitid