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

commit 2993057a94fa0f3954c327d8430943c7f71642ba
Author: Corinna Vinschen <[email protected]>
Date:   Sun Jan 20 22:47:52 2019 +0100

    Cygwin: timerfd: implement TFD_TIMER_CANCEL_ON_SET
    
    Signed-off-by: Corinna Vinschen <[email protected]>

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 *);

Reply via email to