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>
This commit is contained in:
		
							parent
							
								
									b6f53617a7
								
							
						
					
					
						commit
						068182e26c
					
				| 
						 | 
				
			
			@ -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 \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 ())
 | 
			
		||||
	    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  __try
 | 
			
		||||
    {
 | 
			
		||||
      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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
      *timerid = (timer_t) new timer_tracker (clock_id, evp);
 | 
			
		||||
      *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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue