[newlib-cygwin] Cygwin: timerfd: implement TFD_TIMER_CANCEL_ON_SET

Corinna Vinschen corinna@sourceware.org
Sun Jan 20 21:49:00 GMT 2019


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

commit 2993057a94fa0f3954c327d8430943c7f71642ba
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sun Jan 20 22:47:52 2019 +0100

    Cygwin: timerfd: implement TFD_TIMER_CANCEL_ON_SET
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/autoload.cc |  2 ++
 winsup/cygwin/timerfd.cc  | 90 +++++++++++++++++++++++++++++++++++++++++++++--
 winsup/cygwin/timerfd.h   | 28 ++++++++++-----
 3 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 7978287..b4dc773 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -663,6 +663,7 @@ LoadDLLfunc (CreateDesktopW, 24, user32)
 LoadDLLfunc (CreateWindowExW, 48, user32)
 LoadDLLfunc (CreateWindowStationW, 16, user32)
 LoadDLLfunc (DefWindowProcW, 16, user32)
+LoadDLLfunc (DestroyWindow, 4, user32)
 LoadDLLfunc (DispatchMessageW, 4, user32)
 LoadDLLfunc (EmptyClipboard, 0, user32)
 LoadDLLfunc (EnumWindows, 8, user32)
@@ -690,6 +691,7 @@ LoadDLLfunc (SetClipboardData, 8, user32)
 LoadDLLfunc (SetParent, 8, user32)
 LoadDLLfunc (SetProcessWindowStation, 4, user32)
 LoadDLLfunc (SetThreadDesktop, 4, user32)
+LoadDLLfunc (UnregisterClassW, 8, user32)
 
 LoadDLLfuncEx (CreateEnvironmentBlock, 12, userenv, 1)
 LoadDLLfuncEx2 (CreateProfile, 16, userenv, 1, 1)
diff --git a/winsup/cygwin/timerfd.cc b/winsup/cygwin/timerfd.cc
index dbb63e6..cebb002 100644
--- a/winsup/cygwin/timerfd.cc
+++ b/winsup/cygwin/timerfd.cc
@@ -16,6 +16,70 @@ details. */
 #include <sys/timerfd.h>
 #include "timerfd.h"
 
+#define TFD_CANCEL_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
+
+/* Unfortunately MsgWaitForMultipleObjectsEx does not receive WM_TIMECHANGED
+   messages without a window defined in this process.  Create a hidden window
+   for that purpose. */
+
+void
+timerfd_tracker::create_timechange_window ()
+{
+  WNDCLASSW wclass = { 0 };
+  WCHAR cname[NAME_MAX];
+
+  __small_swprintf (cname, L"Cygwin.timerfd.%u", winpid);
+  wclass.lpfnWndProc = DefWindowProcW;
+  wclass.hInstance = GetModuleHandle (NULL);
+  wclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+  wclass.lpszClassName = cname;
+  atom = RegisterClassW (&wclass);
+  if (!atom)
+    debug_printf ("RegisterClass %E");
+  else
+    {
+      window = CreateWindowExW (0, cname, cname, WS_POPUP, 0, 0, 0, 0,
+				NULL, NULL, NULL, NULL);
+      if (!window)
+	debug_printf ("RegisterClass %E");
+    }
+}
+
+void
+timerfd_tracker::delete_timechange_window ()
+{
+  if (window)
+    DestroyWindow (window);
+  if (atom)
+    UnregisterClassW ((LPWSTR) (uintptr_t) atom, GetModuleHandle (NULL));
+}
+
+void
+timerfd_tracker::handle_timechange_window ()
+{
+  MSG msg;
+
+  if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)
+    {
+      DispatchMessageW(&msg);
+      if (msg.message == WM_TIMECHANGE
+	  && get_clockid () == CLOCK_REALTIME
+	  && (flags () & TFD_CANCEL_FLAGS) == TFD_CANCEL_FLAGS
+	  && enter_critical_section ())
+	{
+	  /* make sure to handle each WM_TIMECHANGE only once! */
+	  if (msg.time != tc_time ())
+	    {
+	      set_overrun_count (-1LL);
+	      disarm_timer ();
+	      timer_expired ();
+	      set_tc_time (msg.time);
+	    }
+	  leave_critical_section ();
+	}
+    }
+}
+
 DWORD
 timerfd_tracker::thread_func ()
 {
@@ -23,14 +87,19 @@ timerfd_tracker::thread_func ()
   HANDLE armed[2] = { tfd_shared->arm_evt (),
 		      cancel_evt };
 
+  create_timechange_window ();
   while (1)
     {
-      switch (WaitForMultipleObjects (2, armed, FALSE, INFINITE))
+      switch (MsgWaitForMultipleObjectsEx (2, armed, INFINITE, QS_POSTMESSAGE,
+					   MWMO_INPUTAVAILABLE))
 	{
 	case WAIT_OBJECT_0:
 	  break;
 	case WAIT_OBJECT_0 + 1:
 	  goto canceled;
+	case WAIT_OBJECT_0 + 2:
+	  handle_timechange_window ();
+	  continue;
 	default:
 	  continue;
 	}
@@ -40,9 +109,12 @@ timerfd_tracker::thread_func ()
       HANDLE expired[3] = { tfd_shared->timer (),
 			    tfd_shared->disarm_evt (),
 			    cancel_evt };
+
       while (1)
 	{
-	  switch (WaitForMultipleObjects (3, expired, FALSE, INFINITE))
+	  switch (MsgWaitForMultipleObjectsEx (3, expired, INFINITE,
+					       QS_POSTMESSAGE,
+					       MWMO_INPUTAVAILABLE))
 	    {
 	    case WAIT_OBJECT_0:
 	      break;
@@ -50,12 +122,23 @@ timerfd_tracker::thread_func ()
 	      goto disarmed;
 	    case WAIT_OBJECT_0 + 2:
 	      goto canceled;
+	    case WAIT_OBJECT_0 + 3:
+	      handle_timechange_window ();
+	      continue;
 	    default:
 	      continue;
 	    }
 
 	  if (!enter_critical_section ())
 	    continue;
+	  /* Make sure we haven't been abandoned and/or disarmed
+	     in the meantime */
+	  if (overrun_count () == -1LL
+	      || IsEventSignalled (tfd_shared->disarm_evt ()))
+	    {
+	      leave_critical_section ();
+	      goto disarmed;
+	    }
 	  /* One-shot timer? */
 	  if (!get_interval ())
 	    {
@@ -93,7 +176,7 @@ timerfd_tracker::thread_func ()
 		  NtSetTimer (tfd_shared->timer (), &DueTime, NULL, NULL,
 			      Resume, 0, NULL);
 		}
-	      }
+	    }
 	  /* Arm the expiry object */
 	  timer_expired ();
 	  leave_critical_section ();
@@ -103,6 +186,7 @@ disarmed:
     }
 
 canceled:
+  delete_timechange_window ();
   _my_tls._ctinfo->auto_release ();     /* automatically return the cygthread to the cygthread pool */
   return 0;
 }
diff --git a/winsup/cygwin/timerfd.h b/winsup/cygwin/timerfd.h
index 543fad1..cebd1d9 100644
--- a/winsup/cygwin/timerfd.h
+++ b/winsup/cygwin/timerfd.h
@@ -31,6 +31,7 @@ class timerfd_shared
   LONG64 _interval;		/* timer interval in 100ns */
   LONG64 _overrun_count;	/* expiry counter */
   int _flags;			/* settime flags */
+  DWORD _tc_time;		/* timestamp of the last WM_TIMECHANGE msg */
 
   int create (clockid_t);
   bool dtor ();
@@ -43,7 +44,8 @@ class timerfd_shared
   LONG64 get_clock_now () const { return get_clock (_clockid)->n100secs (); }
   struct itimerspec &time_spec () { return _time_spec; }
   int flags () const { return _flags; }
-  LONG64 overrun_count () const { return _overrun_count; }
+
+  /* write access methods */
   void increment_overrun_count (LONG64 add)
     { InterlockedAdd64 (&_overrun_count, add); }
   void set_overrun_count (LONG64 newval)
@@ -55,8 +57,6 @@ class timerfd_shared
 	ResetEvent (_expired_evt);
       return ret;
     }
-
-  /* write access methods */
   bool enter_cs ()
     {
       return (WaitForSingleObject (_access_mtx, INFINITE) & ~WAIT_ABANDONED_0)
@@ -73,7 +73,7 @@ class timerfd_shared
       memset (&_time_spec, 0, sizeof _time_spec);
       _exp_ts = 0;
       _interval = 0;
-      _flags = 0;
+      /* _flags = 0;  DON'T DO THAT.  Required for TFD_TIMER_CANCEL_ON_SET */
       NtCancelTimer (timer (), NULL);
       SetEvent (_disarm_evt);
       return 0;
@@ -86,8 +86,6 @@ class timerfd_shared
 
 class timerfd_tracker		/* cygheap! */
 {
-  DWORD winpid;			/* This is used @ fork/exec time to know if
-				   this tracker already has been fixed up. */
   HANDLE tfd_shared_hdl;	/* handle auf shared mem */
   timerfd_shared *tfd_shared;	/* pointer auf shared mem, needs
 				   NtMapViewOfSection in each new process. */
@@ -96,6 +94,14 @@ class timerfd_tracker		/* cygheap! */
   HANDLE sync_thr;		/* cygthread sync object. */
   LONG local_instance_count;	/* each open fd increments this.
 				   If 0 -> cancel thread.  */
+  DWORD winpid;			/* This is used @ fork/exec time to know if
+				   this tracker already has been fixed up. */
+  HWND window;			/* window handle */
+  ATOM atom;			/* window class */
+
+  void create_timechange_window ();
+  void delete_timechange_window ();
+  void handle_timechange_window ();
 
   bool dtor ();
 
@@ -107,9 +113,10 @@ class timerfd_tracker		/* cygheap! */
   int disarm_timer () const { return tfd_shared->disarm_timer (); }
   void timer_expired () const { tfd_shared->timer_expired (); }
 
+  LONG64 overrun_count () const { return tfd_shared->_overrun_count; }
   void increment_overrun_count (LONG64 add) const
     { tfd_shared->increment_overrun_count (add); }
-  void set_overrun_count (uint64_t ov_cnt) const
+  void set_overrun_count (LONG64 ov_cnt) const
     { tfd_shared->set_overrun_count ((LONG64) ov_cnt); }
   LONG64 read_and_reset_overrun_count () const
     { return tfd_shared->read_and_reset_overrun_count (); }
@@ -119,9 +126,13 @@ class timerfd_tracker		/* cygheap! */
   struct timespec it_interval () const
     { return tfd_shared->time_spec ().it_interval; }
 
+  clock_t get_clockid () const { return tfd_shared->_clockid; }
   LONG64 get_clock_now () const { return tfd_shared->get_clock_now (); }
   LONG64 get_exp_ts () const { return tfd_shared->_exp_ts; }
   LONG64 get_interval () const { return tfd_shared->_interval; }
+  int flags () const { return tfd_shared->flags (); }
+  DWORD tc_time () const { return tfd_shared->_tc_time; }
+  void set_tc_time (DWORD new_time) { tfd_shared->_tc_time = new_time; }
 
   void set_exp_ts (LONG64 ts) const { tfd_shared->set_exp_ts (ts); }
 
@@ -129,7 +140,8 @@ class timerfd_tracker		/* cygheap! */
   void *operator new (size_t, void *p) __attribute__ ((nothrow)) {return p;}
   timerfd_tracker ()
   : tfd_shared_hdl (NULL), tfd_shared (NULL), cancel_evt (NULL),
-    sync_thr (NULL), local_instance_count (1) {}
+    sync_thr (NULL), local_instance_count (1), winpid (0), window (NULL),
+    atom (0) {}
   int create (clockid_t);
   int gettime (struct itimerspec *);
   int settime (int, const struct itimerspec *, struct itimerspec *);



More information about the Cygwin-cvs mailing list