summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--winsup/cygwin/Makefile.in1
-rw-r--r--winsup/cygwin/fhandler.h2
-rw-r--r--winsup/cygwin/fhandler_timerfd.cc191
-rw-r--r--winsup/cygwin/timer.cc337
-rw-r--r--winsup/cygwin/timer.h31
-rw-r--r--winsup/cygwin/timerfd.cc515
-rw-r--r--winsup/cygwin/timerfd.h160
7 files changed, 907 insertions, 330 deletions
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 8481851c0..374272f4c 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 497612ae1..97804ed30 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 c7ff9c162..8d3085692 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 e817dab81..e24edd5e9 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 b9a072e6d..dd5b165c7 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 000000000..145f3a665
--- /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 000000000..6c42d91f4
--- /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__ */