diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2019-01-12 21:19:52 +0100 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2019-01-12 21:26:07 +0100 |
commit | 9e295a8d193e138808931816f5858761ff08f402 (patch) | |
tree | 62d319602869f3a2ee4991a4234b1eafc8ffe28c /winsup/cygwin | |
parent | 961be8d726a81918d8d34c9dae603e7820a2416f (diff) | |
download | newlib-9e295a8d193e138808931816f5858761ff08f402.zip newlib-9e295a8d193e138808931816f5858761ff08f402.tar.gz newlib-9e295a8d193e138808931816f5858761ff08f402.tar.bz2 |
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>
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/common.din | 1 | ||||
-rw-r--r-- | winsup/cygwin/exceptions.cc | 12 | ||||
-rw-r--r-- | winsup/cygwin/include/cygwin/version.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/include/limits.h | 2 | ||||
-rw-r--r-- | winsup/cygwin/release/2.12.0 | 5 | ||||
-rw-r--r-- | winsup/cygwin/signal.cc | 7 | ||||
-rw-r--r-- | winsup/cygwin/timer.cc | 124 | ||||
-rw-r--r-- | winsup/cygwin/timer.h | 7 |
8 files changed, 149 insertions, 12 deletions
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index d1d9555..a6363e1 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 419b097..8c1b3b4 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 bb1fa97..1bcec33 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 3550c4f..524a469 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 b772782..15f07e0 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 b3e257b..74d6eb6 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 0aeba58..e38decb 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -17,6 +17,10 @@ details. */ #include "timer.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); 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; @@ -373,6 +457,26 @@ timer_settime (timer_t timerid, int flags, } 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) { int ret = -1; diff --git a/winsup/cygwin/timer.h b/winsup/cygwin/timer.h index 0442c37..f75cd48 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 (); |