[newlib-cygwin] Cygwin: posix timers: implement timer_getoverrun

Corinna Vinschen corinna@sourceware.org
Sat Jan 12 20:26:00 GMT 2019


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=9e295a8d193e138808931816f5858761ff08f402

commit 9e295a8d193e138808931816f5858761ff08f402
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sat Jan 12 21:19:52 2019 +0100

    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>

Diff:
---
 winsup/cygwin/common.din               |   1 +
 winsup/cygwin/exceptions.cc            |  12 ++++
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/include/limits.h         |   2 +-
 winsup/cygwin/release/2.12.0           |   5 ++
 winsup/cygwin/signal.cc                |   7 ++
 winsup/cygwin/timer.cc                 | 124 ++++++++++++++++++++++++++++++---
 winsup/cygwin/timer.h                  |   7 ++
 winsup/doc/new-features.xml            |   9 +++
 winsup/doc/posix.xml                   |   2 +-
 10 files changed, 159 insertions(+), 13 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 (&notify_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 ();
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index 6af7270..2e1645f 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -46,6 +46,15 @@ Support Linux-specific open(2) flag O_PATH.
 </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>
 clock_nanosleep, pthread_condattr_setclock and timer_create now support
 all clocks, except CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID.
 </para></listitem>
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index afa1ca4..021b596 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1003,6 +1003,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     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).</para>
     pthread_mutex_consistent
     putmsg
     setnetent
-    timer_getoverrun
     ulimit
     waitid
 </screen>



More information about the Cygwin-cvs mailing list