[newlib-cygwin] Cygwin: timers: implement timerfd

Corinna Vinschen corinna@sourceware.org
Tue Jan 15 21:02:00 GMT 2019


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

commit 068182e26c7b397df579b69a18f745092844d1b4
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Tue Jan 15 22:02:33 2019 +0100

    Cygwin: timers: implement timerfd
    
    First cut of a timerfd implementation.
    
    Still TODO:
    - fork/exec semantics
    - timerfd_settime TFD_TIMER_CANCEL_ON_SET flag
    - ioctl(TFD_IOC_SET_TICKS)
    - bug fixes
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/Makefile.in              |   1 +
 winsup/cygwin/common.din               |   3 +
 winsup/cygwin/devices.cc               |   3 +
 winsup/cygwin/devices.h                |   3 +
 winsup/cygwin/devices.in               |   3 +
 winsup/cygwin/dtable.cc                |   3 +
 winsup/cygwin/fhandler.h               |  51 +++++++
 winsup/cygwin/fhandler_timerfd.cc      | 198 +++++++++++++++++++++++++
 winsup/cygwin/include/cygwin/version.h |   3 +-
 winsup/cygwin/include/sys/timerfd.h    |  45 ++++++
 winsup/cygwin/release/2.12.0           |   3 +-
 winsup/cygwin/select.cc                |  45 ++++++
 winsup/cygwin/timer.cc                 | 254 +++++++++++++++++++++++++++------
 winsup/cygwin/timer.h                  |  21 ++-
 winsup/doc/new-features.xml            |   3 +-
 winsup/doc/posix.xml                   |   3 +
 16 files changed, 591 insertions(+), 51 deletions(-)

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 6147e7c..8481851 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -305,6 +305,7 @@ DLL_OFILES:= \
 	fhandler_socket_unix.o \
 	fhandler_tape.o \
 	fhandler_termios.o \
+	fhandler_timerfd.o \
 	fhandler_tty.o \
 	fhandler_virtual.o \
 	fhandler_windows.o \
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index b7f39f9..384cf0b 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -1488,6 +1488,9 @@ timer_delete SIGFE
 timer_getoverrun SIGFE
 timer_gettime SIGFE
 timer_settime SIGFE
+timerfd_create SIGFE
+timerfd_gettime SIGFE
+timerfd_settime SIGFE
 times SIGFE
 timezone SIGFE
 timingsafe_bcmp NOSIGFE
diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc
index 31fd64f..2e31ca3 100644
--- a/winsup/cygwin/devices.cc
+++ b/winsup/cygwin/devices.cc
@@ -123,6 +123,9 @@ const _device dev_pipew_storage =
 const _device dev_signalfd_storage =
   {"", {FH_SIGNALFD}, "", exists_internal};
 
+const _device dev_timerfd_storage =
+  {"", {FH_TIMERFD}, "", exists_internal};
+
 const _device dev_socket_storage =
   {"", {FH_SOCKET}, "", exists_internal};
 
diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h
index 065f77e..47156f2 100644
--- a/winsup/cygwin/devices.h
+++ b/winsup/cygwin/devices.h
@@ -73,6 +73,7 @@ enum fh_devices
   FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
 
   FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
+  FH_TIMERFD = FHDEV (DEV_VIRTFS_MAJOR, 14),
 
   DEV_FLOPPY_MAJOR = 2,
   FH_FLOPPY  = FHDEV (DEV_FLOPPY_MAJOR, 0),
@@ -404,6 +405,8 @@ extern const _device dev_af_unix_storage;
 
 extern const _device dev_signalfd_storage;
 #define signalfd_dev ((device *) &dev_signalfd_storage)
+extern const _device dev_timerfd_storage;
+#define timerfd_dev ((device *) &dev_timerfd_storage)
 extern const _device dev_piper_storage;
 #define piper_dev ((device *) &dev_piper_storage)
 extern const _device dev_pipew_storage;
diff --git a/winsup/cygwin/devices.in b/winsup/cygwin/devices.in
index 79a7fe7..59f5f00 100644
--- a/winsup/cygwin/devices.in
+++ b/winsup/cygwin/devices.in
@@ -119,6 +119,9 @@ const _device dev_pipew_storage =
 const _device dev_signalfd_storage =
   {"", {FH_SIGNALFD}, "", exists_internal};
 
+const _device dev_timerfd_storage =
+  {"", {FH_TIMERFD}, "", exists_internal};
+
 const _device dev_socket_storage =
   {"", {FH_SOCKET}, "", exists_internal};
 
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index c8aecfa..663f99b 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -578,6 +578,9 @@ fh_alloc (path_conv& pc)
 	case FH_SIGNALFD:
 	  fh = cnew (fhandler_signalfd);
 	  break;
+	case FH_TIMERFD:
+	  fh = cnew (fhandler_timerfd);
+	  break;
 	case FH_TTY:
 	  if (!pc.isopen ())
 	    {
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index f4a8b54..898f85d 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -421,6 +421,7 @@ public:
   virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
   virtual class fhandler_console *is_console () { return 0; }
   virtual class fhandler_signalfd *is_signalfd () { return NULL; }
+  virtual class fhandler_timerfd *is_timerfd () { return NULL; }
   virtual int is_windows () {return 0; }
 
   virtual void __reg3 raw_read (void *ptr, size_t& ulen);
@@ -2673,6 +2674,55 @@ class fhandler_signalfd : public fhandler_base
   }
 };
 
+class fhandler_timerfd : public fhandler_base
+{
+  timer_t timerid;
+
+ public:
+  fhandler_timerfd ();
+  fhandler_timerfd (void *) {}
+
+  fhandler_timerfd *is_timerfd () { return this; }
+
+  char *get_proc_fd_name (char *buf);
+
+  int timerfd (clockid_t clock_id, int flags);
+  int settime (int flags, const struct itimerspec *value,
+	       struct itimerspec *ovalue);
+  int gettime (struct itimerspec *ovalue);
+
+  int __reg2 fstat (struct stat *buf);
+  void __reg3 read (void *ptr, size_t& len);
+  int dup (fhandler_base *child, int);
+  int ioctl (unsigned int, void *);
+  int close ();
+
+  HANDLE get_timerfd_handle ();
+
+  void fixup_after_fork_exec (bool);
+  void fixup_after_exec () {fixup_after_fork_exec (true);}
+  void fixup_after_fork (HANDLE) {fixup_after_fork_exec (false);}
+
+  select_record *select_read (select_stuff *);
+  select_record *select_write (select_stuff *);
+  select_record *select_except (select_stuff *);
+
+  void copyto (fhandler_base *x)
+  {
+    x->pc.free_strings ();
+    *reinterpret_cast<fhandler_timerfd *> (x) = *this;
+    x->reset (this);
+  }
+
+  fhandler_timerfd *clone (cygheap_types malloc_type = HEAP_FHANDLER)
+  {
+    void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_timerfd));
+    fhandler_timerfd *fh = new (ptr) fhandler_timerfd (ptr);
+    copyto (fh);
+    return fh;
+  }
+};
+
 struct fhandler_nodevice: public fhandler_base
 {
   fhandler_nodevice ();
@@ -2713,6 +2763,7 @@ typedef union
   char __registry[sizeof (fhandler_registry)];
   char __serial[sizeof (fhandler_serial)];
   char __signalfd[sizeof (fhandler_signalfd)];
+  char __timerfd[sizeof (fhandler_timerfd)];
   char __socket_inet[sizeof (fhandler_socket_inet)];
   char __socket_local[sizeof (fhandler_socket_local)];
 #ifdef __WITH_AF_UNIX
diff --git a/winsup/cygwin/fhandler_timerfd.cc b/winsup/cygwin/fhandler_timerfd.cc
new file mode 100644
index 0000000..b88c797
--- /dev/null
+++ b/winsup/cygwin/fhandler_timerfd.cc
@@ -0,0 +1,198 @@
+/* fhandler_timerfd.cc: fhandler for timerfd
+
+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 "timer.h"
+#include <sys/timerfd.h>
+#include <cygwin/signal.h>
+
+fhandler_timerfd::fhandler_timerfd () :
+  fhandler_base ()
+{
+}
+
+char *
+fhandler_timerfd::get_proc_fd_name (char *buf)
+{
+  return strcpy (buf, "anon_inode:[timerfd]");
+}
+
+int
+fhandler_timerfd::timerfd (clockid_t clock_id, int flags)
+{
+  timerid = (timer_t) new timer_tracker (clock_id, NULL, true);
+  if (flags & TFD_NONBLOCK)
+    set_nonblocking (true);
+  if (flags & TFD_CLOEXEC)
+    set_close_on_exec (true);
+  if (get_unique_id () == 0)
+    {
+      nohandle (true);
+      set_unique_id ();
+      set_ino (get_unique_id ());
+    }
+  return 0;
+}
+
+int
+fhandler_timerfd::settime (int flags, const itimerspec *value,
+			   itimerspec *ovalue)
+{
+  int ret = -1;
+
+  __try
+    {
+      timer_tracker *tt = (timer_tracker *) timerid;
+      ret = tt->settime (flags, value, ovalue);
+    }
+  __except (EFAULT) {}
+  __endtry
+  return ret;
+}
+
+int
+fhandler_timerfd::gettime (itimerspec *ovalue)
+{
+  int ret = -1;
+
+  __try
+    {
+      timer_tracker *tt = (timer_tracker *) timerid;
+      tt->gettime (ovalue);
+      ret = 0;
+    }
+  __except (EFAULT) {}
+  __endtry
+  return ret;
+}
+
+int __reg2
+fhandler_timerfd::fstat (struct stat *buf)
+{
+  int ret = fhandler_base::fstat (buf);
+  if (!ret)
+    {
+      buf->st_mode = S_IRUSR | S_IWUSR;
+      buf->st_dev = FH_TIMERFD;
+      buf->st_ino = get_unique_id ();
+    }
+  return ret;
+}
+
+void __reg3
+fhandler_timerfd::read (void *ptr, size_t& len)
+{
+  if (len < sizeof (LONG64))
+    {
+      set_errno (EINVAL);
+      len = (size_t) -1;
+      return;
+    }
+
+  __try
+    {
+      timer_tracker *tt = (timer_tracker *) timerid;
+      LONG64 ret = tt->wait (is_nonblocking ());
+      if (ret == -1)
+	__leave;
+      PLONG64 pl64 = (PLONG64) ptr;
+      *pl64 = ret + 1;
+      len = sizeof (LONG64);
+      return;
+    }
+  __except (EFAULT) {}
+  __endtry
+  len = (size_t) -1;
+  return;
+}
+
+HANDLE
+fhandler_timerfd::get_timerfd_handle ()
+{
+  __try
+    {
+      timer_tracker *tt = (timer_tracker *) timerid;
+      return tt->get_timerfd_handle ();
+    }
+  __except (EFAULT) {}
+  __endtry
+  return NULL;
+}
+
+int
+fhandler_timerfd::dup (fhandler_base *child, int flags)
+{
+  int ret = fhandler_base::dup (child, flags);
+
+  if (!ret)
+    {
+      fhandler_timerfd *fhc = (fhandler_timerfd *) child;
+      __try
+	{
+	  timer_tracker *tt = (timer_tracker *) fhc->timerid;
+	  tt->increment_instances ();
+	  ret = 0;
+	}
+      __except (EFAULT) {}
+      __endtry
+    }
+  return ret;
+}
+
+void
+fhandler_timerfd::fixup_after_fork_exec (bool execing)
+{
+  if (!execing)
+    {
+      /* TODO after fork */
+    }
+  else if (!close_on_exec ())
+    {
+      /* TODO after exec */
+    }
+}
+
+int
+fhandler_timerfd::ioctl (unsigned int cmd, void *p)
+{
+  int ret = -1;
+
+  switch (cmd)
+    {
+    case TFD_IOC_SET_TICKS:
+      /* TODO */
+      ret = 0;
+      break;
+    default:
+      set_errno (EINVAL);
+      break;
+    }
+  syscall_printf ("%d = ioctl_timerfd(%x, %p)", ret, cmd, p);
+  return ret;
+}
+
+int
+fhandler_timerfd::close ()
+{
+  int ret = -1;
+
+  __try
+    {
+      timer_tracker *tt = (timer_tracker *) timerid;
+      timer_tracker::close (tt);
+      ret = 0;
+    }
+  __except (EFAULT) {}
+  __endtry
+  return ret;
+}
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h
index ec5f55f..d6dcbea 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -503,12 +503,13 @@ details. */
        CLOCK_BOOTTIME.
   331: Add timer_getoverrun, DELAYTIMER_MAX.
   332: Add signalfd.
+  333: Add timerfd_create, timerfd_gettime, timerfd_settime.
 
   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 332
+#define CYGWIN_VERSION_API_MINOR 333
 
 /* 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/sys/timerfd.h b/winsup/cygwin/include/sys/timerfd.h
new file mode 100644
index 0000000..bf3ce2a
--- /dev/null
+++ b/winsup/cygwin/include/sys/timerfd.h
@@ -0,0 +1,45 @@
+/* sys/timerfd.h: define timerfd_create(2) and friends
+
+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	_SYS_TIMERFD_H
+#define	_SYS_TIMERFD_H
+
+#include <time.h>
+#include <sys/_default_fcntl.h>
+#include <asm/socket.h>
+
+enum
+{
+  /* timerfd_create */
+  TFD_CLOEXEC = O_CLOEXEC,
+  TFD_NONBLOCK = O_NONBLOCK,
+  /* timerfd_settime */
+  TFD_TIMER_ABSTIME = TIMER_ABSTIME,
+  TFD_TIMER_CANCEL_ON_SET = (TIMER_ABSTIME << 1)
+};
+#define TFD_CLOEXEC TFD_CLOEXEC
+#define TFD_NONBLOCK TFD_NONBLOCK
+#define TFD_TIMER_ABSTIME TFD_TIMER_ABSTIME
+#define TFD_TIMER_CANCEL_ON_SET TFD_TIMER_CANCEL_ON_SET
+
+#define TFD_IOC_SET_TICKS	_IOW('T', 0, __uint64_t)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int timerfd_create (clockid_t, int);
+extern int timerfd_settime (int, int, const struct itimerspec *,
+			    struct itimerspec *);
+extern int timerfd_gettime (int, struct itimerspec *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_TIMERFD_H */
diff --git a/winsup/cygwin/release/2.12.0 b/winsup/cygwin/release/2.12.0
index a0dce6f..80d0c6f 100644
--- a/winsup/cygwin/release/2.12.0
+++ b/winsup/cygwin/release/2.12.0
@@ -27,7 +27,8 @@ What's new:
 - Support overrun counter for posix timers (via timer_getoverrun() or
   siginfo_t::si_overrun).
 
-- New API: signalfd, timer_getoverrun.
+- New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
+  timer_getoverrun.
 
 
 What changed:
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index d1ae3c6..d6757e4 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -1870,3 +1870,48 @@ fhandler_signalfd::select_except (select_stuff *stuff)
   s->except_ready = false;
   return s;
 }
+
+select_record *
+fhandler_timerfd::select_read (select_stuff *stuff)
+{
+  select_record *s = stuff->start.next;
+  if (!s->startup)
+    {
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->h = get_timerfd_handle ();
+  s->read_selected = true;
+  s->read_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_timerfd::select_write (select_stuff *stuff)
+{
+  select_record *s = stuff->start.next;
+  if (!s->startup)
+    {
+      s->startup = no_startup;
+      s->verify = no_verify;
+    }
+  s->peek = NULL;
+  s->write_selected = false;
+  s->write_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_timerfd::select_except (select_stuff *stuff)
+{
+  select_record *s = stuff->start.next;
+  if (!s->startup)
+    {
+      s->startup = no_startup;
+      s->verify = no_verify;
+    }
+  s->peek = NULL;
+  s->except_selected = false;
+  s->except_ready = false;
+  return s;
+}
diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc
index e38decb..93e4714 100644
--- a/winsup/cygwin/timer.cc
+++ b/winsup/cygwin/timer.cc
@@ -15,13 +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
 
-timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL);
+timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL, false);
 
 class lock_timer_tracker
 {
@@ -58,34 +59,45 @@ timer_tracker::cancel ()
 
 timer_tracker::~timer_tracker ()
 {
+  HANDLE hdl;
+
+  deleting = true;
   if (cancel ())
     {
-      CloseHandle (hcancel);
-#ifdef DEBUGGING
-      hcancel = NULL;
-#endif
+      HANDLE hdl = InterlockedExchangePointer (&hcancel, NULL);
+      CloseHandle (hdl);
+      hdl = InterlockedExchangePointer (&timerfd_event, NULL);
+      if (hdl)
+	CloseHandle (hdl);
     }
-  if (syncthread)
-    CloseHandle (syncthread);
+  hdl = InterlockedExchangePointer (&syncthread, NULL);
+  if (hdl)
+    CloseHandle (hdl);
   magic = 0;
 }
 
-timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
+/* 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), event_running (EVENT_DISARMED),
+  overrun_count_curr (0), overrun_count (0)
 {
   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;
     }
-  clock_id = c;
-  magic = TT_MAGIC;
-  hcancel = NULL;
-  event_running = EVENT_DISARMED;
-  overrun_count_curr = 0;
-  overrun_count = 0;
+  if (fd)
+    timerfd_event = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
   if (this != &ttstart)
     {
       lock_timer_tracker here;
@@ -94,6 +106,16 @@ timer_tracker::timer_tracker (clockid_t c, const sigevent *e)
     }
 }
 
+void timer_tracker::increment_instances ()
+{
+  InterlockedIncrement (&instance_count);
+}
+
+LONG timer_tracker::decrement_instances ()
+{
+  return InterlockedDecrement (&instance_count);
+}
+
 static inline int64_t
 timespec_to_us (const timespec& ts)
 {
@@ -118,8 +140,8 @@ timer_tracker::arm_event ()
   return ret;
 }
 
-LONG
-timer_tracker::disarm_event ()
+LONG64
+timer_tracker::_disarm_event ()
 {
   LONG ret;
 
@@ -128,13 +150,8 @@ timer_tracker::disarm_event ()
     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;
+      InterlockedExchange64 (&overrun_count_curr, overrun_count);
       ret = overrun_count_curr;
       InterlockedExchange64 (&overrun_count, 0);
       InterlockedExchange (&event_running, EVENT_DISARMED);
@@ -142,6 +159,51 @@ 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, timerfd_event };
+  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)
 {
@@ -198,6 +260,14 @@ timer_tracker::thread_func ()
 
       switch (evp.sigev_notify)
 	{
+	case SIGEV_NONE:
+	  {
+	    if (!timerfd_event)
+	      break;
+	    arm_event ();
+	    SetEvent (timerfd_event);
+	    break;
+	  }
 	case SIGEV_SIGNAL:
 	  {
 	    if (arm_event ())
@@ -350,9 +420,17 @@ 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)
       {
@@ -362,9 +440,26 @@ 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::fixup_after_fork ()
 {
+  /* TODO: Keep timerfd timers available and restart them */
   ttstart.hcancel = ttstart.syncthread = NULL;
   ttstart.event_running = EVENT_DISARMED;
   ttstart.overrun_count_curr = 0;
@@ -412,21 +507,21 @@ timer_create (clockid_t clock_id, struct sigevent *__restrict evp,
 {
   int ret = -1;
 
-  __try
+  if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
     {
-      if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id))
-	{
-	  set_errno (ENOTSUP);
-	  return -1;
-	}
+      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;
+    }
 
-      *timerid = (timer_t) new timer_tracker (clock_id, evp);
+  __try
+    {
+      *timerid = (timer_t) new timer_tracker (clock_id, evp, false);
       ret = 0;
     }
   __except (EFAULT) {}
@@ -489,15 +584,7 @@ timer_delete (timer_t timerid)
 	  set_errno (EINVAL);
 	  __leave;
 	}
-
-      lock_timer_tracker here;
-      if (in_tt->clean_and_unhook () == 0)
-	{
-	  delete in_tt;
-	  ret = 0;
-	}
-      else
-	set_errno (EINVAL);
+      ret = timer_tracker::close (in_tt);
     }
   __except (EFAULT) {}
   __endtry
@@ -604,3 +691,84 @@ ualarm (useconds_t value, useconds_t interval)
  syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval);
  return ret;
 }
+
+extern "C"
+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)
+{
+  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 f75cd48..2a31e3e 100644
--- a/winsup/cygwin/timer.h
+++ b/winsup/cygwin/timer.h
@@ -14,35 +14,46 @@ 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;
-  LONG overrun_count_curr;
+  LONG64 overrun_count_curr;
   LONG64 overrun_count;
 
   bool cancel ();
+  LONG decrement_instances ();
+  int clean_and_unhook ();
+  LONG64 _disarm_event ();
 
  public:
-  timer_tracker (clockid_t, const sigevent *);
+  timer_tracker (clockid_t, const sigevent *, bool);
   ~timer_tracker ();
   inline bool is_timer_tracker () const { return magic == TT_MAGIC; }
+
+  void increment_instances ();
+  LONG64 wait (bool nonblocking);
+  HANDLE get_timerfd_handle () const { return timerfd_event; }
+
   inline sigevent_t *sigevt () { return &evp; }
-  inline int getoverrun () const { return overrun_count_curr; }
+  inline LONG64 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 ();
+  unsigned int disarm_event ();
 
   DWORD thread_func ();
   static void fixup_after_fork ();
+  static int close (timer_tracker *tt);
 };
 
 #endif /* __TIMER_H__ */
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index d4fc745..2c4b3e4 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -51,7 +51,8 @@ siginfo_t::si_overrun).
 </para></listitem>
 
 <listitem><para>
-New API: signalfd, timer_getoverrun.
+New APIs: signalfd, timerfd_create, timerfd_gettime, timerfd_settime,
+timer_getoverrun.
 </para></listitem>
 
 <listitem><para>
diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml
index 365a8f0..8e9b1a5 100644
--- a/winsup/doc/posix.xml
+++ b/winsup/doc/posix.xml
@@ -1394,6 +1394,9 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
     strverscmp
     sysinfo
     tdestroy
+    timerfd_create
+    timerfd_gettime
+    timerfd_settime
     timegm
     timelocal
     toascii_l



More information about the Cygwin-cvs mailing list