diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 898f85db2..f0c612d8a 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2699,9 +2699,7 @@ class fhandler_timerfd : public fhandler_base 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);} + void fixup_after_exec (); select_record *select_read (select_stuff *); select_record *select_write (select_stuff *); diff --git a/winsup/cygwin/fhandler_timerfd.cc b/winsup/cygwin/fhandler_timerfd.cc index b88c797ef..8ce91af86 100644 --- a/winsup/cygwin/fhandler_timerfd.cc +++ b/winsup/cygwin/fhandler_timerfd.cc @@ -27,10 +27,19 @@ fhandler_timerfd::get_proc_fd_name (char *buf) return strcpy (buf, "anon_inode:[timerfd]"); } +/* The timers connected to a descriptor are stored on the cygheap + together with their fhandler. */ + +#define cnew(name, ...) \ + ({ \ + void* ptr = (void*) ccalloc (HEAP_FHANDLER, 1, sizeof (name)); \ + ptr ? new (ptr) name (__VA_ARGS__) : NULL; \ + }) + int fhandler_timerfd::timerfd (clockid_t clock_id, int flags) { - timerid = (timer_t) new timer_tracker (clock_id, NULL, true); + timerid = (timer_t) cnew (timer_tracker, clock_id, NULL, true); if (flags & TFD_NONBLOCK) set_nonblocking (true); if (flags & TFD_CLOEXEC) @@ -150,13 +159,9 @@ fhandler_timerfd::dup (fhandler_base *child, int flags) } void -fhandler_timerfd::fixup_after_fork_exec (bool execing) +fhandler_timerfd::fixup_after_exec () { - if (!execing) - { - /* TODO after fork */ - } - else if (!close_on_exec ()) + if (!close_on_exec ()) { /* TODO after exec */ } diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index c429af6ee..bd412f0e2 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -22,7 +22,8 @@ details. */ #define EVENT_ARMED -1 #define EVENT_LOCK 1 -timer_tracker NO_COPY ttstart (CLOCK_REALTIME, NULL, false); +/* Must not be NO_COPY, otherwise timerfd breaks. */ +timer_tracker ttstart (CLOCK_REALTIME, NULL, false); class lock_timer_tracker { @@ -79,7 +80,8 @@ timer_tracker::~timer_tracker () /* 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), + hcancel (NULL), syncthread (NULL), timerfd_event (NULL), + interval_us(0), sleepto_us(0), event_running (EVENT_DISARMED), overrun_count_curr (0), overrun_count (0) { if (e != NULL) @@ -171,7 +173,7 @@ timer_tracker::disarm_event () LONG64 timer_tracker::wait (bool nonblocking) { - HANDLE w4[3] = { NULL, hcancel, timerfd_event }; + HANDLE w4[3] = { NULL, hcancel, get_timerfd_handle () }; LONG64 ret = -1; wait_signal_arrived here (w4[0]); @@ -264,7 +266,11 @@ timer_tracker::thread_func () { if (!timerfd_event) break; - arm_event (); + if (arm_event ()) + { + debug_printf ("%p timerfd already queued", this); + break; + } SetEvent (timerfd_event); break; } @@ -443,20 +449,41 @@ timer_tracker::close (timer_tracker *tt) 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); + } +} + void timer_tracker::fixup_after_fork () { - /* TODO: Keep timerfd timers available and restart them */ ttstart.hcancel = ttstart.syncthread = NULL; + ttstart.interval_us = ttstart.sleepto_us = 0; ttstart.event_running = EVENT_DISARMED; - ttstart.overrun_count_curr = 0; - ttstart.overrun_count = 0; + ttstart.overrun_count_curr = ttstart.overrun_count = 0; for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */) { timer_tracker *deleteme = tt->next; - tt->next = deleteme->next; - deleteme->hcancel = deleteme->syncthread = NULL; - delete deleteme; + 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; + } } } @@ -731,6 +758,8 @@ 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); diff --git a/winsup/cygwin/timer.h b/winsup/cygwin/timer.h index 595d9fe9b..f4c89bc6b 100644 --- a/winsup/cygwin/timer.h +++ b/winsup/cygwin/timer.h @@ -33,8 +33,14 @@ class timer_tracker LONG decrement_instances (); 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 (); inline bool is_timer_tracker () const { return magic == TT_MAGIC; }