[newlib-cygwin] Cygwin: timerfd: reimplement from scratch

Corinna Vinschen corinna@sourceware.org
Sat Jan 19 19:00:00 GMT 2019


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

commit 40481dbabb1cce2c33a67ccb5ddb368ee3491562
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Sat Jan 19 19:53:48 2019 +0100

    Cygwin: timerfd: reimplement from scratch
    
    Using posix timers "timer_tracker" as base class for timerfd was flawed.
    Posix timers are not inherited by child processes and don't survive
    execve.  The method used by posix timers didn't allow to share timers
    between processes. The timers were still per-process timers and worked
    entirely separate from each other.  Reading from these timers via
    different descriptors was only synchronized within the same process.
    
    This does not reflect the timerfd semantics in Linux: The per-file
    timers can be dup'ed and survive fork and execve.  They are still just
    descriptors pointing to the same timer object originally created by
    timerfd_create.  Synchronization is performed between all descriptor
    instances of the same timer, system-wide.
    
    Thus, reimplement timerfd using a timer instance in shared memory,
    a kernel timer, and a handful of sync objects.
    
    Every process maintains a per-process timerfd struct on the cygheap
    maintaining a per-process thread.  Every process sharing the same
    timerfd will run this thread checking the state of the timer, similar
    to the posix timer thread, just working on the shared objects and
    synchronizing its job with each other thread.
    
    Drop the timerfd implementation in the posix timer code and move the
    public API to fhandler_timerfd.c.  Keep the ttstart timer_tracker
    anchor out of "NO_COPY" since the fixup_after_fork code should run to
    avoid memory leakage.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/Makefile.in         |   1 +
 winsup/cygwin/fhandler.h          |   2 +
 winsup/cygwin/fhandler_timerfd.cc | 191 +++++++++++---
 winsup/cygwin/timer.cc            | 337 +++++--------------------
 winsup/cygwin/timer.h             |  31 +--
 winsup/cygwin/timerfd.cc          | 515 ++++++++++++++++++++++++++++++++++++++
 winsup/cygwin/timerfd.h           | 160 ++++++++++++
 7 files changed, 907 insertions(+), 330 deletions(-)

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 8481851..374272f 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -396,6 +396,7 @@ DLL_OFILES:= \
 	termios.o \
 	thread.o \
 	timer.o \
+	timerfd.o \
 	times.o \
 	tls_pbuf.o \
 	tty.o \
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 497612a..97804ed 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2682,6 +2682,7 @@ class fhandler_timerfd : public fhandler_base
  public:
   fhandler_timerfd ();
   fhandler_timerfd (void *) {}
+  ~fhandler_timerfd ();
 
   fhandler_timerfd *is_timerfd () { return this; }
 
@@ -2701,6 +2702,7 @@ class fhandler_timerfd : public fhandler_base
 
   HANDLE get_timerfd_handle ();
 
+  void fixup_after_fork (HANDLE);
   void fixup_after_exec ();
 
   select_record *select_read (select_stuff *);
diff --git a/winsup/cygwin/fhandler_timerfd.cc b/winsup/cygwin/fhandler_timerfd.cc
index c7ff9c1..8d30856 100644
--- a/winsup/cygwin/fhandler_timerfd.cc
+++ b/winsup/cygwin/fhandler_timerfd.cc
@@ -1,4 +1,4 @@
-/* fhandler_timerfd.cc: fhandler for timerfd
+/* fhandler_timerfd.cc: fhandler for timerfd, public timerfd API
 
 This file is part of Cygwin.
 
@@ -12,7 +12,7 @@ details. */
 #include "pinfo.h"
 #include "dtable.h"
 #include "cygheap.h"
-#include "timer.h"
+#include "timerfd.h"
 #include <sys/timerfd.h>
 #include <cygwin/signal.h>
 
@@ -39,7 +39,19 @@ fhandler_timerfd::get_proc_fd_name (char *buf)
 int
 fhandler_timerfd::timerfd (clockid_t clock_id, int flags)
 {
-  timerid = (timer_t) cnew (timer_tracker, clock_id, NULL, true);
+  timerfd_tracker *tfd = cnew (timerfd_tracker);
+  if (!tfd)
+    {
+      set_errno (ENOMEM);
+      return -1;
+    }
+  int ret = tfd->create (clock_id);
+  if (ret < 0)
+    {
+      cfree (tfd);
+      set_errno (-ret);
+      return -1;
+    }
   if (flags & TFD_NONBLOCK)
     set_nonblocking (true);
   if (flags & TFD_CLOEXEC)
@@ -48,19 +60,25 @@ fhandler_timerfd::timerfd (clockid_t clock_id, int flags)
   set_unique_id ();
   set_ino (get_unique_id ());
   set_flags (O_RDWR | O_BINARY);
+  timerid = (timer_t) tfd;
   return 0;
 }
 
 int
-fhandler_timerfd::settime (int flags, const itimerspec *value,
-			   itimerspec *ovalue)
+fhandler_timerfd::settime (int flags, const struct itimerspec *new_value,
+			   struct itimerspec *old_value)
 {
   int ret = -1;
 
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      ret = tt->settime (flags, value, ovalue);
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      ret = tfd->settime (flags, new_value, old_value);
+      if (ret < 0)
+	{
+	  set_errno (-ret);
+	  ret = -1;
+	}
     }
   __except (EFAULT) {}
   __endtry
@@ -68,15 +86,19 @@ fhandler_timerfd::settime (int flags, const itimerspec *value,
 }
 
 int
-fhandler_timerfd::gettime (itimerspec *ovalue)
+fhandler_timerfd::gettime (struct itimerspec *ovalue)
 {
   int ret = -1;
 
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      tt->gettime (ovalue);
-      ret = 0;
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      ret = tfd->gettime (ovalue);
+      if (ret < 0)
+	{
+	  set_errno (-ret);
+	  ret = -1;
+	}
     }
   __except (EFAULT) {}
   __endtry
@@ -108,12 +130,14 @@ fhandler_timerfd::read (void *ptr, size_t& len)
 
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      LONG64 ret = tt->wait (is_nonblocking ());
-      if (ret == -1)
-	__leave;
-      PLONG64 pl64 = (PLONG64) ptr;
-      *pl64 = ret + 1;
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      LONG64 ret = tfd->wait (is_nonblocking ());
+      if (ret < 0)
+	{
+	  set_errno (-ret);
+	  __leave;
+	}
+      *(PLONG64) ptr = ret;
       len = sizeof (LONG64);
       return;
     }
@@ -135,8 +159,8 @@ fhandler_timerfd::get_timerfd_handle ()
 {
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      return tt->get_timerfd_handle ();
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      return tfd->get_timerfd_handle ();
     }
   __except (EFAULT) {}
   __endtry
@@ -153,8 +177,8 @@ fhandler_timerfd::dup (fhandler_base *child, int flags)
       fhandler_timerfd *fhc = (fhandler_timerfd *) child;
       __try
 	{
-	  timer_tracker *tt = (timer_tracker *) fhc->timerid;
-	  tt->increment_instances ();
+	  timerfd_tracker *tfd = (timerfd_tracker *) fhc->timerid;
+	  tfd->increment_instances ();
 	  ret = 0;
 	}
       __except (EFAULT) {}
@@ -164,14 +188,27 @@ fhandler_timerfd::dup (fhandler_base *child, int flags)
 }
 
 void
+fhandler_timerfd::fixup_after_fork (HANDLE)
+{
+  __try
+    {
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      tfd->fixup_after_fork ();
+    }
+  __except (EFAULT) {}
+  __endtry
+}
+
+void
 fhandler_timerfd::fixup_after_exec ()
 {
-  if (close_on_exec ())
-    return;
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      tt->fixup_after_exec ();
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      if (close_on_exec ())
+	tfd->decrement_instances ();
+      else
+	tfd->fixup_after_exec ();
     }
   __except (EFAULT) {}
   __endtry
@@ -188,7 +225,7 @@ fhandler_timerfd::ioctl (unsigned int cmd, void *p)
     case TFD_IOC_SET_TICKS:
       __try
 	{
-	  timer_tracker *tt = (timer_tracker *) timerid;
+	  timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 
 	  ov_cnt = *(uint64_t *) p;
 	  if (!ov_cnt)
@@ -196,11 +233,11 @@ fhandler_timerfd::ioctl (unsigned int cmd, void *p)
 	      set_errno (EINVAL);
 	      break;
 	    }
-	  tt->set_event (ov_cnt);
+	  tfd->ioctl_set_ticks (ov_cnt);
+	  ret = 0;
 	}
       __except (EFAULT) {}
       __endtry
-      ret = 0;
       break;
     default:
       ret = fhandler_base::ioctl (cmd, p);
@@ -210,6 +247,17 @@ fhandler_timerfd::ioctl (unsigned int cmd, void *p)
   return ret;
 }
 
+fhandler_timerfd::~fhandler_timerfd ()
+{
+  __try
+    {
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      timerfd_tracker::dtor (tfd);
+    }
+  __except (EFAULT) {}
+  __endtry
+}
+
 int
 fhandler_timerfd::close ()
 {
@@ -217,11 +265,94 @@ fhandler_timerfd::close ()
 
   __try
     {
-      timer_tracker *tt = (timer_tracker *) timerid;
-      timer_tracker::close (tt);
+      timerfd_tracker *tfd = (timerfd_tracker *) timerid;
+      tfd->close ();
       ret = 0;
     }
   __except (EFAULT) {}
   __endtry
   return ret;
 }
+
+extern "C" int
+timerfd_create (clockid_t clock_id, int flags)
+{
+  int ret = -1;
+  fhandler_timerfd *fh;
+
+  debug_printf ("timerfd_create (%lu, %y)", clock_id, flags);
+
+  if (clock_id != CLOCK_REALTIME
+      && clock_id != CLOCK_MONOTONIC
+      && clock_id != CLOCK_BOOTTIME)
+    {
+      set_errno (EINVAL);
+      goto done;
+    }
+  if ((flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) != 0)
+    {
+      set_errno (EINVAL);
+      goto done;
+    }
+
+    {
+      /* Create new timerfd descriptor. */
+      cygheap_fdnew fd;
+
+      if (fd < 0)
+        goto done;
+      fh = (fhandler_timerfd *) build_fh_dev (*timerfd_dev);
+      if (fh && fh->timerfd (clock_id, flags) == 0)
+        {
+          fd = fh;
+          if (fd <= 2)
+            set_std_handle (fd);
+          ret = fd;
+        }
+      else
+        delete fh;
+    }
+
+done:
+  syscall_printf ("%R = timerfd_create (%lu, %y)", ret, clock_id, flags);
+  return ret;
+}
+
+extern "C" int
+timerfd_settime (int fd_in, int flags, const struct itimerspec *value,
+		 struct itimerspec *ovalue)
+{
+  /* TODO: There's no easy way to implement TFD_TIMER_CANCEL_ON_SET,
+     but we should at least accept the flag. */
+  if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+
+  cygheap_fdget fd (fd_in);
+  if (fd < 0)
+    return -1;
+  fhandler_timerfd *fh = fd->is_timerfd ();
+  if (!fh)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  return fh->settime (flags, value, ovalue);
+}
+
+extern "C" int
+timerfd_gettime (int fd_in, struct itimerspec *ovalue)
+{
+  cygheap_fdget fd (fd_in);
+  if (fd < 0)
+    return -1;
+  fhandler_timerfd *fh = fd->is_timerfd ();
+  if (!fh)
+    {
+      set_errno (EINVAL);
+      return -1;
+    }
+  return fh->gettime (ovalue);
+}
diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc
index e817dab..e24edd5 100644
--- a/winsup/cygwin/timer.cc
+++ b/winsup/cygwin/timer.cc
@@ -15,15 +15,14 @@ details. */
 #include "dtable.h"
 #include "cygheap.h"
 #include "timer.h"
-#include <sys/timerfd.h>
 #include <sys/param.h>
 
 #define EVENT_DISARMED	 0
 #define EVENT_ARMED	-1
 #define EVENT_LOCK	 1
 
-/* Must not be NO_COPY, otherwise timerfd breaks. */
-timer_tracker ttstart (CLOCK_REALTIME, NULL, false);
+/* Must not be NO_COPY to avoid memory leak after fork. */
+timer_tracker ttstart (CLOCK_REALTIME, NULL);
 
 class lock_timer_tracker
 {
@@ -60,46 +59,34 @@ timer_tracker::cancel ()
 
 timer_tracker::~timer_tracker ()
 {
-  HANDLE hdl;
-
-  deleting = true;
   if (cancel ())
     {
-      HANDLE hdl = InterlockedExchangePointer (&hcancel, NULL);
-      CloseHandle (hdl);
-      hdl = InterlockedExchangePointer (&timerfd_event, NULL);
-      if (hdl)
-	CloseHandle (hdl);
+      CloseHandle (hcancel);
+#ifdef DEBUGGING
+      hcancel = NULL;
+#endif
     }
-  hdl = InterlockedExchangePointer (&syncthread, NULL);
-  if (hdl)
-    CloseHandle (hdl);
+  if (syncthread)
+    CloseHandle (syncthread);
   magic = 0;
 }
 
-/* fd is true for timerfd timers. */
-timer_tracker::timer_tracker (clockid_t c, const sigevent *e, bool fd)
-: magic (TT_MAGIC), instance_count (1), clock_id (c), deleting (false),
-  hcancel (NULL), syncthread (NULL), timerfd_event (NULL),
-  interval_us(0), sleepto_us(0), event_running (EVENT_DISARMED),
-  overrun_count_curr (0), overrun_count (0)
+timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
 {
   if (e != NULL)
     evp = *e;
-  else if (fd)
-    {
-      evp.sigev_notify = SIGEV_NONE;
-      evp.sigev_signo = 0;
-      evp.sigev_value.sival_ptr = this;
-    }
   else
     {
       evp.sigev_notify = SIGEV_SIGNAL;
       evp.sigev_signo = SIGALRM;
       evp.sigev_value.sival_ptr = this;
     }
-  if (fd)
-    timerfd_event = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+  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;
@@ -132,24 +119,8 @@ timer_tracker::arm_event ()
   return ret;
 }
 
-void
-timer_tracker::set_event (uint64_t ov_cnt)
-{
-  LONG ret;
-
-  while ((ret = InterlockedCompareExchange (&event_running, EVENT_LOCK,
-					    EVENT_DISARMED)) == EVENT_LOCK)
-    yield ();
-  InterlockedExchange64 (&overrun_count, ov_cnt);
-  if (ret == EVENT_DISARMED)
-    {
-      SetEvent (get_timerfd_handle ());
-      InterlockedExchange (&event_running, EVENT_ARMED);
-    }
-}
-
-LONG64
-timer_tracker::_disarm_event ()
+LONG
+timer_tracker::disarm_event ()
 {
   LONG ret;
 
@@ -158,7 +129,13 @@ timer_tracker::_disarm_event ()
     yield ();
   if (ret == EVENT_ARMED)
     {
-      InterlockedExchange64 (&overrun_count_curr, overrun_count);
+      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);
@@ -166,51 +143,6 @@ timer_tracker::_disarm_event ()
   return ret;
 }
 
-unsigned int
-timer_tracker::disarm_event ()
-{
-  LONG64 ov = _disarm_event ();
-  if (ov > DELAYTIMER_MAX || ov < 0)
-    return DELAYTIMER_MAX;
-  return (unsigned int) ov;
-}
-
-LONG64
-timer_tracker::wait (bool nonblocking)
-{
-  HANDLE w4[3] = { NULL, hcancel, get_timerfd_handle () };
-  LONG64 ret = -1;
-
-  wait_signal_arrived here (w4[0]);
-repeat:
-  switch (WaitForMultipleObjects (3, w4, FALSE, nonblocking ? 0 : INFINITE))
-    {
-    case WAIT_OBJECT_0:		/* signal */
-      if (_my_tls.call_signal_handler ())
-	goto repeat;
-      set_errno (EINTR);
-      break;
-    case WAIT_OBJECT_0 + 1:	/* settime oder timer delete */
-      if (deleting)
-	{
-	  set_errno (EIO);
-	  break;
-	}
-      /*FALLTHRU*/
-    case WAIT_OBJECT_0 + 2:	/* timer event */
-      ret = _disarm_event ();
-      ResetEvent (timerfd_event);
-      break;
-    case WAIT_TIMEOUT:
-      set_errno (EAGAIN);
-      break;
-    default:
-      __seterrno ();
-      break;
-    }
-  return ret;
-}
-
 static void *
 notify_thread_wrapper (void *arg)
 {
@@ -267,18 +199,6 @@ timer_tracker::thread_func ()
 
       switch (evp.sigev_notify)
 	{
-	case SIGEV_NONE:
-	  {
-	    if (!timerfd_event)
-	      break;
-	    if (arm_event ())
-	      {
-		debug_printf ("%p timerfd already queued", this);
-		break;
-	      }
-	    SetEvent (timerfd_event);
-	    break;
-	  }
 	case SIGEV_SIGNAL:
 	  {
 	    if (arm_event ())
@@ -342,6 +262,17 @@ timer_thread (VOID *x)
   return tt->thread_func ();
 }
 
+static inline bool
+timespec_bad (const timespec& t)
+{
+  if (t.tv_nsec < 0 || t.tv_nsec >= NSPERSEC || t.tv_sec < 0)
+    {
+      set_errno (EINVAL);
+      return true;
+    }
+  return false;
+}
+
 int
 timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalue)
 {
@@ -355,12 +286,8 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu
 	  __leave;
 	}
 
-      if (!valid_timespec (value->it_value)
-	  || !valid_timespec (value->it_interval))
-	{
-	  set_errno (EINVAL);
-	  __leave;
-	}
+      if (timespec_bad (value->it_value) || timespec_bad (value->it_interval))
+	__leave;
 
       lock_timer_tracker here;
       cancel ();
@@ -411,17 +338,9 @@ timer_tracker::gettime (itimerspec *ovalue)
     }
 }
 
-/* Returns
-
-    1 if we still have to keep the timer around
-    0 if we can delete the timer
-   -1 if we can't find the timer in the list
-*/
 int
 timer_tracker::clean_and_unhook ()
 {
-  if (decrement_instances () > 0)
-    return 1;
   for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
     if (tt->next == this)
       {
@@ -431,74 +350,19 @@ timer_tracker::clean_and_unhook ()
   return -1;
 }
 
-int
-timer_tracker::close (timer_tracker *tt)
-{
-  lock_timer_tracker here;
-  int ret = tt->clean_and_unhook ();
-  if (ret >= 0)
-    {
-      if (ret == 0)
-	delete tt;
-      ret = 0;
-    }
-  else
-    set_errno (EINVAL);
-  return ret;
-}
-
-void
-timer_tracker::restart ()
-{
-  timerfd_event = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
-  if (interval_us != 0 || sleepto_us != 0)
-    {
-      hcancel = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
-      syncthread = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
-      new cygthread (timer_thread, this, "itimer", syncthread);
-    }
-}
-
-/* Only called from fhandler_timerfd::fixup_after_exec.  Note that
-   we don't touch the instance count.  This is handled by closing
-   the timer from fhandler_timerfd::close on O_CLOEXEC.  Ultimately
-   the instance count should be correct after execve. */
-void
-timer_tracker::fixup_after_exec ()
-{
-  lock_timer_tracker here;
-  /* Check if timer is already in the list. If so, skip it. */
-  for (timer_tracker *tt = &ttstart; tt->next != NULL; tt = tt->next)
-    if (tt->next == this)
-      return;
-  next = ttstart.next;
-  ttstart.next = this;
-  restart ();
-}
-
 void
 timer_tracker::fixup_after_fork ()
 {
   ttstart.hcancel = ttstart.syncthread = NULL;
-  ttstart.interval_us = ttstart.sleepto_us = 0;
   ttstart.event_running = EVENT_DISARMED;
-  ttstart.overrun_count_curr = ttstart.overrun_count = 0;
+  ttstart.overrun_count_curr = 0;
+  ttstart.overrun_count = 0;
   for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */)
     {
       timer_tracker *deleteme = tt->next;
-      if (deleteme->get_timerfd_handle ())
-	{
-	  tt = deleteme;
-	  tt->restart ();
-	}
-      else
-	{
-	  tt->next = deleteme->next;
-	  deleteme->timerfd_event = NULL;
-	  deleteme->hcancel = NULL;
-	  deleteme->syncthread = NULL;
-	  delete deleteme;
-	}
+      tt->next = deleteme->next;
+      deleteme->hcancel = deleteme->syncthread = NULL;
+      delete deleteme;
     }
 }
 
@@ -536,21 +400,21 @@ timer_create (clockid_t clock_id, struct sigevent *__restrict evp,
 {
   int ret = -1;
 
-  if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
+  __try
     {
-      set_errno (ENOTSUP);
-      return -1;
-    }
+      if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
+	{
+	  set_errno (ENOTSUP);
+	  return -1;
+	}
 
-  if (clock_id >= MAX_CLOCKS)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
+      if (clock_id >= MAX_CLOCKS)
+	{
+	  set_errno (EINVAL);
+	  return -1;
+	}
 
-  __try
-    {
-      *timerid = (timer_t) new timer_tracker (clock_id, evp, false);
+      *timerid = (timer_t) new timer_tracker (clock_id, evp);
       ret = 0;
     }
   __except (EFAULT) {}
@@ -617,7 +481,15 @@ timer_delete (timer_t timerid)
 	  set_errno (EINVAL);
 	  __leave;
 	}
-      ret = timer_tracker::close (in_tt);
+
+      lock_timer_tracker here;
+      if (in_tt->clean_and_unhook () == 0)
+	{
+	  delete in_tt;
+	  ret = 0;
+	}
+      else
+	set_errno (EINVAL);
     }
   __except (EFAULT) {}
   __endtry
@@ -724,86 +596,3 @@ ualarm (useconds_t value, useconds_t interval)
  syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval);
  return ret;
 }
-
-extern "C" int
-timerfd_create (clockid_t clock_id, int flags)
-{
-  int ret = -1;
-  fhandler_timerfd *fh;
-
-  debug_printf ("timerfd (%lu, %y)", clock_id, flags);
-
-  if (clock_id != CLOCK_REALTIME
-      && clock_id != CLOCK_MONOTONIC
-      && clock_id != CLOCK_BOOTTIME)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-  if ((flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) != 0)
-    {
-      set_errno (EINVAL);
-      goto done;
-    }
-
-    {
-      /* Create new timerfd descriptor. */
-      cygheap_fdnew fd;
-
-      if (fd < 0)
-        goto done;
-      fh = (fhandler_timerfd *) build_fh_dev (*timerfd_dev);
-      if (fh && fh->timerfd (clock_id, flags) == 0)
-        {
-          fd = fh;
-          if (fd <= 2)
-            set_std_handle (fd);
-          ret = fd;
-        }
-      else
-        delete fh;
-    }
-
-done:
-  syscall_printf ("%R = timerfd (%lu, %y)", ret, clock_id, flags);
-  return ret;
-}
-
-extern "C" int
-timerfd_settime (int fd_in, int flags, const struct itimerspec *value,
-		 struct itimerspec *ovalue)
-{
-  /* There's no easy way to implement TFD_TIMER_CANCEL_ON_SET, but
-     we should at least accept the flag. */
-  if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-
-  cygheap_fdget fd (fd_in);
-  if (fd < 0)
-    return -1;
-  fhandler_timerfd *fh = fd->is_timerfd ();
-  if (!fh)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-  return fh->settime (flags, value, ovalue);
-}
-
-extern "C" int
-timerfd_gettime (int fd_in, struct itimerspec *ovalue)
-{
-  cygheap_fdget fd (fd_in);
-  if (fd < 0)
-    return -1;
-  fhandler_timerfd *fh = fd->is_timerfd ();
-  if (!fh)
-    {
-      set_errno (EINVAL);
-      return -1;
-    }
-  return fh->gettime (ovalue);
-}
diff --git a/winsup/cygwin/timer.h b/winsup/cygwin/timer.h
index b9a072e..dd5b165 100644
--- a/winsup/cygwin/timer.h
+++ b/winsup/cygwin/timer.h
@@ -14,56 +14,35 @@ class timer_tracker
 {
   unsigned magic;
   timer_tracker *next;
-  LONG instance_count;
 
   clockid_t clock_id;
   sigevent evp;
   timespec it_interval;
-  bool deleting;
   HANDLE hcancel;
   HANDLE syncthread;
-  HANDLE timerfd_event;
   int64_t interval_us;
   int64_t sleepto_us;
   LONG event_running;
-  LONG64 overrun_count_curr;
+  LONG overrun_count_curr;
   LONG64 overrun_count;
 
   bool cancel ();
-  LONG decrement_instances () { return InterlockedDecrement (&instance_count); }
-  int clean_and_unhook ();
-  LONG64 _disarm_event ();
-  void restart ();
 
  public:
-  void *operator new (size_t size) __attribute__ ((nothrow))
-  { return malloc (size); }
-  void *operator new (size_t, void *p) __attribute__ ((nothrow)) {return p;}
-  void operator delete(void *p) { incygheap (p) ? cfree (p) : free (p); }
-
-  timer_tracker (clockid_t, const sigevent *, bool);
+  timer_tracker (clockid_t, const sigevent *);
   ~timer_tracker ();
   inline bool is_timer_tracker () const { return magic == TT_MAGIC; }
-
-
-  void increment_instances () { InterlockedIncrement (&instance_count); }
-  LONG64 wait (bool nonblocking);
-  HANDLE get_timerfd_handle () const { return timerfd_event; }
-
   inline sigevent_t *sigevt () { return &evp; }
-  inline LONG64 getoverrun () const { return overrun_count_curr; }
+  inline int getoverrun () const { return overrun_count_curr; }
 
   void gettime (itimerspec *);
   int settime (int, const itimerspec *, itimerspec *);
-
+  int clean_and_unhook ();
   LONG arm_event ();
-  void set_event (uint64_t ov_cnt);
-  unsigned int disarm_event ();
+  LONG disarm_event ();
 
   DWORD thread_func ();
-  void fixup_after_exec ();
   static void fixup_after_fork ();
-  static int close (timer_tracker *tt);
 };
 
 extern void fixup_timers_after_fork ();
diff --git a/winsup/cygwin/timerfd.cc b/winsup/cygwin/timerfd.cc
new file mode 100644
index 0000000..145f3a6
--- /dev/null
+++ b/winsup/cygwin/timerfd.cc
@@ -0,0 +1,515 @@
+/* timerfd.cc: timerfd helper classes
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include "path.h"
+#include "fhandler.h"
+#include "pinfo.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "cygerrno.h"
+#include <sys/timerfd.h>
+#include "timerfd.h"
+
+DWORD
+timerfd_tracker::thread_func ()
+{
+  /* Outer loop: Is the timer armed?  If not, wait for it. */
+  HANDLE armed[2] = { tfd_shared->arm_evt (),
+		      cancel_evt };
+
+  while (1)
+    {
+      switch (WaitForMultipleObjects (2, armed, FALSE, INFINITE))
+	{
+	case WAIT_OBJECT_0:
+	  break;
+	case WAIT_OBJECT_0 + 1:
+	  goto canceled;
+	default:
+	  continue;
+	}
+
+      /* Inner loop: Timer expired?  If not, wait for it. */
+      HANDLE expired[3] = { tfd_shared->timer (),
+			    tfd_shared->disarm_evt (),
+			    cancel_evt };
+      while (1)
+	{
+	  switch (WaitForMultipleObjects (3, expired, FALSE, INFINITE))
+	    {
+	    case WAIT_OBJECT_0:
+	      break;
+	    case WAIT_OBJECT_0 + 1:
+	      goto disarmed;
+	    case WAIT_OBJECT_0 + 2:
+	      goto canceled;
+	    default:
+	      continue;
+	    }
+
+	  if (!enter_critical_section ())
+	    continue;
+	  /* One-shot timer? */
+	  if (!get_interval ())
+	    {
+	      /* Set overrun count, disarm timer */
+	      increment_overrun_count (1);
+	      disarm_timer ();
+	    }
+	  else
+	    {
+	      /* Compute overrun count. */
+	      LONG64 now = get_clock_now ();
+	      LONG64 ts = get_exp_ts ();
+
+	      increment_overrun_count ((now - ts + get_interval () - 1)
+				       / get_interval ());
+	      /* Set exp_ts to current timestamp.  Make sure exp_ts ends up
+		 bigger than "now" and fix overrun count as required */
+	      while ((ts += get_interval ()) <= (now = get_clock_now ()))
+		increment_overrun_count ((now - ts + get_interval () - 1)
+					 / get_interval ());
+	      set_exp_ts (ts);
+	      /* NtSetTimer allows periods of up to 24 days only.  If the time
+		 is longer, we set the timer up as one-shot timer for each
+		 interval.  Restart timer here with new due time. */
+	      if (get_interval () > INT_MAX * (NS100PERSEC / MSPERSEC))
+		{
+		  /* TODO: CLOCK_REALTIME_ALARM / CLOCK_BOOTTIME_ALARM
+		     See comment in arm_timer */
+		  BOOL Resume = FALSE;
+		  LARGE_INTEGER DueTime = { QuadPart: -get_interval () };
+
+		  NtSetTimer (tfd_shared->timer (), &DueTime, NULL, NULL,
+			      Resume, 0, NULL);
+		}
+	      }
+	  /* Arm the expiry object */
+	  timer_expired ();
+	  leave_critical_section ();
+	}
+disarmed:
+      ;
+    }
+
+canceled:
+  _my_tls._ctinfo->auto_release ();     /* automatically return the cygthread to the cygthread pool */
+  return 0;
+}
+
+static DWORD WINAPI
+timerfd_thread (VOID *arg)
+{
+  timerfd_tracker *tt = ((timerfd_tracker *) arg);
+  return tt->thread_func ();
+}
+
+int
+timerfd_shared::create (clockid_t clock_id)
+{
+  int ret;
+  NTSTATUS status;
+  OBJECT_ATTRIBUTES attr;
+
+  /* Create access mutex */
+  InitializeObjectAttributes (&attr, NULL, OBJ_INHERIT, NULL,
+			      sec_none.lpSecurityDescriptor);
+  status = NtCreateMutant (&_access_mtx, MUTEX_ALL_ACCESS, &attr, FALSE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err;
+    }
+  /* Create "timer is armed" event, set to "Unsignaled" at creation time */
+  status = NtCreateEvent (&_arm_evt, EVENT_ALL_ACCESS, &attr,
+			  NotificationEvent, FALSE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_close_access_mtx;
+    }
+  /* Create "timer is disarmed" event, set to "Signaled" at creation time */
+  status = NtCreateEvent (&_disarm_evt, EVENT_ALL_ACCESS, &attr,
+			  NotificationEvent, TRUE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_close_arm_evt;
+    }
+  /* Create timer */
+  status = NtCreateTimer (&_timer, TIMER_ALL_ACCESS, &attr,
+			  SynchronizationTimer);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_close_disarm_evt;
+    }
+  /* Create "timer expired" semaphore */
+  status = NtCreateEvent (&_expired_evt, EVENT_ALL_ACCESS, &attr,
+			  NotificationEvent, FALSE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_close_timer;
+    }
+  instance_count = 1;
+  _clockid = clock_id;
+  return 0;
+
+err_close_timer:
+  NtClose (_timer);
+err_close_disarm_evt:
+  NtClose (_disarm_evt);
+err_close_arm_evt:
+  NtClose (_arm_evt);
+err_close_access_mtx:
+  NtClose (_access_mtx);
+err:
+  return ret;
+}
+
+int
+timerfd_tracker::create (clockid_t clock_id)
+{
+  int ret;
+  clk_t *clock;
+  NTSTATUS status;
+  OBJECT_ATTRIBUTES attr;
+
+  const ACCESS_MASK access = STANDARD_RIGHTS_REQUIRED
+			     | SECTION_MAP_READ | SECTION_MAP_WRITE;
+  SIZE_T vsize = PAGE_SIZE;
+  LARGE_INTEGER sectionsize = { QuadPart: PAGE_SIZE };
+
+  /* Valid clock? */
+  clock = get_clock (clock_id);
+  if (!clock)
+    {
+      ret = -EINVAL;
+      goto err;
+    }
+  /* Create shared section. */
+  InitializeObjectAttributes (&attr, NULL, OBJ_INHERIT, NULL,
+			      sec_none.lpSecurityDescriptor);
+  status = NtCreateSection (&tfd_shared_hdl, access, &attr,
+			    &sectionsize, PAGE_READWRITE,
+			    SEC_COMMIT, NULL);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err;
+    }
+  /* Create section mapping (has to be repeated after fork/exec */
+  status = NtMapViewOfSection (tfd_shared_hdl, NtCurrentProcess (),
+			       (void **) &tfd_shared, 0, PAGE_SIZE, NULL,
+			       &vsize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_close_tfd_shared_hdl;
+    }
+  /* Create cancel even for this processes timer thread */
+  InitializeObjectAttributes (&attr, NULL, 0, NULL,
+			      sec_none_nih.lpSecurityDescriptor);
+  status = NtCreateEvent (&cancel_evt, EVENT_ALL_ACCESS, &attr,
+			  NotificationEvent, FALSE);
+  if (!NT_SUCCESS (status))
+    {
+      ret = -geterrno_from_nt_status (status);
+      goto err_unmap_tfd_shared;
+    }
+  ret = tfd_shared->create (clock_id);
+  if (ret < 0)
+    goto err_close_cancel_evt;
+  winpid = GetCurrentProcessId ();
+  new cygthread (timerfd_thread, this, "timerfd", sync_thr);
+  return 0;
+
+err_close_cancel_evt:
+  NtClose (cancel_evt);
+err_unmap_tfd_shared:
+  NtUnmapViewOfSection (NtCurrentProcess (), tfd_shared);
+err_close_tfd_shared_hdl:
+  NtClose (tfd_shared_hdl);
+err:
+  return ret;
+}
+
+/* Return true if this was the last instance of a timerfd, session-wide,
+   false otherwise */
+bool
+timerfd_shared::dtor ()
+{
+  if (instance_count > 0)
+    {
+      return false;
+    }
+  timer_expired ();
+  disarm_timer ();
+  NtClose (_timer);
+  NtClose (_arm_evt);
+  NtClose (_disarm_evt);
+  NtClose (_expired_evt);
+  NtClose (_access_mtx);
+  return true;
+}
+
+/* Return true if this was the last instance of a timerfd, session-wide,
+   false otherwise.  Basically this is a destructor, but one which may
+   notify the caller NOT to deleted the object. */
+bool
+timerfd_tracker::dtor ()
+{
+  if (enter_critical_section ())
+    {
+      if (local_instance_count > 0)
+	{
+	  leave_critical_section ();
+	  return false;
+	}
+      SetEvent (cancel_evt);
+      WaitForSingleObject (sync_thr, INFINITE);
+      if (tfd_shared->dtor ())
+	{
+	  NtUnmapViewOfSection (NtCurrentProcess (), tfd_shared);
+	  NtClose (tfd_shared_hdl);
+	}
+      else
+	leave_critical_section ();
+    }
+  NtClose (cancel_evt);
+  NtClose (sync_thr);
+  return true;
+}
+
+void
+timerfd_tracker::dtor (timerfd_tracker *tfd)
+{
+  if (tfd->dtor ())
+    cfree (tfd);
+}
+
+void
+timerfd_tracker::close ()
+{
+  InterlockedDecrement (&local_instance_count);
+  InterlockedDecrement (&tfd_shared->instance_count);
+}
+
+void
+timerfd_tracker::ioctl_set_ticks (uint64_t ov_cnt)
+{
+  set_overrun_count (ov_cnt);
+  timer_expired ();
+}
+
+void
+timerfd_tracker::fixup_after_fork_exec (bool execing)
+{
+  NTSTATUS status;
+  OBJECT_ATTRIBUTES attr;
+  SIZE_T vsize = PAGE_SIZE;
+
+  /* Run this only once per process */
+  if (winpid == GetCurrentProcessId ())
+    return;
+  /* Recreate shared section mapping */
+  status = NtMapViewOfSection (tfd_shared_hdl, NtCurrentProcess (),
+			       (void **) &tfd_shared, 0, PAGE_SIZE, NULL,
+			       &vsize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
+  if (!NT_SUCCESS (status))
+    api_fatal ("Can't recreate shared timerfd section during %s!",
+	       execing ? "execve" : "fork");
+  /* Increment global instance count by the number of instances in this
+     process */
+  InterlockedAdd (&tfd_shared->instance_count, local_instance_count);
+  /* Create cancel even for this processes timer thread */
+  InitializeObjectAttributes (&attr, NULL, 0, NULL,
+			      sec_none_nih.lpSecurityDescriptor);
+  status = NtCreateEvent (&cancel_evt, EVENT_ALL_ACCESS, &attr,
+			  NotificationEvent, FALSE);
+  if (!NT_SUCCESS (status))
+    api_fatal ("Can't recreate timerfd cancel event during %s!",
+	       execing ? "execve" : "fork");
+  /* Set winpid so we don't run this twice */
+  winpid = GetCurrentProcessId ();
+  new cygthread (timerfd_thread, this, "timerfd", sync_thr);
+}
+
+LONG64
+timerfd_tracker::wait (bool nonblocking)
+{
+  HANDLE w4[2] = { get_timerfd_handle (), NULL };
+  LONG64 ret;
+
+  wait_signal_arrived here (w4[1]);
+repeat:
+  switch (WaitForMultipleObjects (2, w4, FALSE, nonblocking ? 0 : INFINITE))
+    {
+    case WAIT_OBJECT_0:		/* timer event */
+      if (!enter_critical_section ())
+	ret = -EIO;
+      else
+	{
+	  ret = read_and_reset_overrun_count ();
+	  leave_critical_section ();
+	  if (ret)
+	    break;
+	  /* A 0 overrun count indicates another read was quicker.
+	     Continue as if we didn't catch the expiry. */
+	  if (!nonblocking)
+	    {
+	      Sleep (100L);
+	      goto repeat;
+	    }
+	  ret = -EAGAIN;
+	}
+      break;
+    case WAIT_OBJECT_0 + 1:	/* signal */
+      if (_my_tls.call_signal_handler ())
+        goto repeat;
+      ret = -EINTR;
+      break;
+    case WAIT_TIMEOUT:
+      ret = -EAGAIN;
+      break;
+    default:
+      ret = -geterrno_from_win_error ();
+      break;
+    }
+  return ret;
+}
+
+int
+timerfd_tracker::gettime (struct itimerspec *curr_value)
+{
+  int ret = 0;
+
+  __try
+    {
+      if (!enter_critical_section ())
+	{
+	  ret = -EBADF;
+	  __leave;
+	}
+      LONG64 next_relative_exp = get_exp_ts () - get_clock_now ();
+      curr_value->it_value.tv_sec = next_relative_exp / NS100PERSEC;
+      next_relative_exp -= curr_value->it_value.tv_sec * NS100PERSEC;
+      curr_value->it_value.tv_nsec = next_relative_exp
+				     * (NSPERSEC / NS100PERSEC);
+      leave_critical_section ();
+      ret = 0;
+    }
+  __except (NO_ERROR)
+    {
+      ret = -EFAULT;
+    }
+  __endtry
+  return ret;
+}
+
+int
+timerfd_shared::arm_timer (int flags, const struct itimerspec *new_value)
+{
+  LONG64 ts;
+  NTSTATUS status;
+  LARGE_INTEGER DueTime;
+  BOOLEAN Resume;
+  LONG Period;
+
+  ResetEvent (_disarm_evt);
+
+  /* Convert incoming itimerspec into 100ns interval and timestamp */
+  _interval = new_value->it_interval.tv_sec * NS100PERSEC
+	      + (new_value->it_interval.tv_nsec + (NSPERSEC / NS100PERSEC) - 1)
+		/ (NSPERSEC / NS100PERSEC);
+  ts = new_value->it_value.tv_sec * NS100PERSEC
+	    + (new_value->it_value.tv_nsec + (NSPERSEC / NS100PERSEC) - 1)
+	      / (NSPERSEC / NS100PERSEC);
+  _flags = flags;
+  if (flags & TFD_TIMER_ABSTIME)
+    {
+      if (_clockid == CLOCK_REALTIME)
+	DueTime.QuadPart = ts + FACTOR;
+      else /* non-REALTIME clocks require relative DueTime. */
+	{
+	  DueTime.QuadPart = ts - get_clock_now ();
+	  /* If the timestamp was earlier than now, compute number
+	     of overruns and offset DueTime to expire immediately. */
+	  if (DueTime.QuadPart >= 0)
+	    {
+	      LONG64 num_intervals = DueTime.QuadPart / _interval;
+	      increment_overrun_count (num_intervals);
+	      DueTime.QuadPart = -1LL;
+	    }
+	}
+    }
+  else
+    {
+      /* Keep relative timestamps relative for the timer, but store the
+         expiry timestamp absolute for the timer thread. */
+      DueTime.QuadPart = -ts;
+      ts += get_clock_now ();
+    }
+  set_exp_ts (ts);
+  /* TODO: CLOCK_REALTIME_ALARM / CLOCK_BOOTTIME_ALARM
+	   Note: Advanced Power Settings -> Sleep -> Allow Wake Timers
+	   since W10 1709 */
+  Resume = FALSE;
+  if (_interval > INT_MAX * (NS100PERSEC / MSPERSEC))
+    Period = 0;
+  else
+    Period = (_interval + (NS100PERSEC / MSPERSEC) - 1)
+	     / (NS100PERSEC / MSPERSEC);
+  status = NtSetTimer (timer (), &DueTime, NULL, NULL, Resume, Period, NULL);
+  if (!NT_SUCCESS (status))
+    {
+      disarm_timer ();
+      return -geterrno_from_nt_status (status);
+    }
+
+  SetEvent (_arm_evt);
+  return 0;
+}
+
+int
+timerfd_tracker::settime (int flags, const struct itimerspec *new_value,
+			  struct itimerspec *old_value)
+{
+  int ret = 0;
+
+  __try
+    {
+      if (!valid_timespec (new_value->it_value)
+          || !valid_timespec (new_value->it_interval))
+	{
+	  ret = -EINVAL;
+	  __leave;
+	}
+      if (!enter_critical_section ())
+	{
+	  ret = -EBADF;
+	  __leave;
+	}
+      if (old_value)
+	gettime (old_value);
+      if (new_value->it_value.tv_sec == 0 && new_value->it_value.tv_nsec == 0)
+	ret = disarm_timer ();
+      else
+	ret = arm_timer (flags, new_value);
+      leave_critical_section ();
+      ret = 0;
+    }
+  __except (NO_ERROR)
+    {
+      ret = -EFAULT;
+    }
+  __endtry
+  return ret;
+}
diff --git a/winsup/cygwin/timerfd.h b/winsup/cygwin/timerfd.h
new file mode 100644
index 0000000..6c42d91
--- /dev/null
+++ b/winsup/cygwin/timerfd.h
@@ -0,0 +1,160 @@
+/* timerfd.h: Define timerfd classes
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef __TIMERFD_H__
+#define __TIMERFD_H__
+
+#include "clock.h"
+#include "ntdll.h"
+
+class timerfd_shared
+{
+  HANDLE _access_mtx;		/* controls access to shared data */
+  HANDLE _arm_evt;		/* settimer sets event when timer is armed,
+				   unsets event when timer gets disarmed. */
+  HANDLE _disarm_evt;		/* settimer sets event when timer is armed,
+				   unsets event when timer gets disarmed. */
+  HANDLE _timer;		/* SynchronizationTimer */
+  HANDLE _expired_evt;		/* Signal if timer expired, Unsignal on read. */
+  LONG instance_count;		/* each open fd increments this.
+				   If 0 -> delete timerfd_shared */
+
+  clockid_t _clockid;		/* clockid */
+  struct itimerspec _time_spec;	/* original incoming itimerspec */
+  LARGE_INTEGER _exp_ts;	/* start timestamp or next expire timestamp
+				   in 100ns */
+  LONG64 _interval;		/* timer interval in 100ns */
+  LONG64 _overrun_count;	/* expiry counter */
+  int _flags;			/* settime flags */
+
+  int create (clockid_t);
+  bool dtor ();
+
+  /* read access methods */
+  HANDLE arm_evt () const { return _arm_evt; }
+  HANDLE disarm_evt () const { return _disarm_evt; }
+  HANDLE timer () const { return _timer; }
+  HANDLE expired_evt () const { return _expired_evt; }
+  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; }
+  void increment_overrun_count (LONG64 add)
+    { InterlockedAdd64 (&_overrun_count, add); }
+  void set_overrun_count (LONG64 newval)
+    { InterlockedExchange64 (&_overrun_count, newval); }
+  LONG64 read_and_reset_overrun_count ()
+    {
+      LONG64 ret = InterlockedExchange64 (&_overrun_count, 0);
+      if (ret)
+	ResetEvent (_expired_evt);
+      return ret;
+    }
+
+  /* write access methods */
+  bool enter_cs ()
+    {
+      return WaitForSingleObject (_access_mtx, INFINITE) == WAIT_OBJECT_0;
+    }
+  void leave_cs ()
+    {
+      ReleaseMutex (_access_mtx);
+    }
+  int arm_timer (int, const struct itimerspec *);
+  int disarm_timer ()
+    {
+      ResetEvent (_arm_evt);
+      memset (&_time_spec, 0, sizeof _time_spec);
+      _exp_ts.QuadPart = 0;
+      _interval = 0;
+      _flags = 0;
+      NtCancelTimer (timer (), NULL);
+      SetEvent (_disarm_evt);
+      return 0;
+    }
+  void timer_expired () { SetEvent (_expired_evt); }
+  void set_exp_ts (LONG64 ts) { _exp_ts.QuadPart = ts; }
+
+  friend class timerfd_tracker;
+};
+
+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. */
+
+  HANDLE cancel_evt;		/* Signal thread to exit. */
+  HANDLE sync_thr;		/* cygthread sync object. */
+  LONG local_instance_count;	/* each open fd increments this.
+				   If 0 -> cancel thread.  */
+
+  bool dtor ();
+
+  bool enter_critical_section () const { return tfd_shared->enter_cs (); }
+  void leave_critical_section () const { tfd_shared->leave_cs (); }
+
+  int arm_timer (int flags, const struct itimerspec *new_value) const
+    { return tfd_shared->arm_timer (flags, new_value); }
+  int disarm_timer () const { return tfd_shared->disarm_timer (); }
+  void timer_expired () const { tfd_shared->timer_expired (); }
+
+  void increment_overrun_count (LONG64 add) const
+    { tfd_shared->increment_overrun_count (add); }
+  void set_overrun_count (uint64_t 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 (); }
+
+  struct timespec it_value () const
+    { return tfd_shared->time_spec ().it_value; }
+  struct timespec it_interval () const
+    { return tfd_shared->time_spec ().it_interval; }
+
+  LONG64 get_clock_now () const { return tfd_shared->get_clock_now (); }
+  LONG64 get_exp_ts () const { return tfd_shared->_exp_ts.QuadPart; }
+  LONG64 get_interval () const { return tfd_shared->_interval; }
+
+  void set_exp_ts (LONG64 ts) const { tfd_shared->set_exp_ts (ts); }
+
+ public:
+  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) {}
+  int create (clockid_t);
+  int gettime (struct itimerspec *);
+  int settime (int, const struct itimerspec *, struct itimerspec *);
+  static void dtor (timerfd_tracker *);
+  void close ();
+  void ioctl_set_ticks (uint64_t);
+  void fixup_after_fork_exec (bool);
+  void fixup_after_fork () { fixup_after_fork_exec (false); }
+  void fixup_after_exec () { fixup_after_fork_exec (true); }
+  HANDLE get_timerfd_handle () const { return tfd_shared->expired_evt (); }
+  HANDLE get_disarm_evt () const { return tfd_shared->disarm_evt (); }
+  LONG64 wait (bool);
+  void increment_global_instances ()
+    { InterlockedIncrement (&tfd_shared->instance_count); }
+  void increment_instances ()
+    {
+      InterlockedIncrement (&tfd_shared->instance_count);
+      InterlockedIncrement (&local_instance_count);
+    }
+  void decrement_instances ()
+    {
+      InterlockedDecrement (&tfd_shared->instance_count);
+      InterlockedDecrement (&local_instance_count);
+    }
+
+  DWORD thread_func ();
+};
+
+#endif /* __TIMERFD_H__ */



More information about the Cygwin-cvs mailing list