Cygwin: timerfd: implement fork semantics

- Puzzeling: Commit ec98d19a08
  changed ttstart to NO_COPY but kept all the code to handle
  fixup after fork.  Revert to not-NO_COPY and make timerfd
  fork work.

- On fixup_after_fork, keep timerfd timers and restart thread
  if they were armed in the parent.

- Move timerfd timer_trackers to cygheap.  Overload timer_tracker
  new and delete methods to handle timers accordingly.  This is not
  exactly required for fork, but exec will be grateful.

- Give up on TFD_TIMER_CANCEL_ON_SET for now.  There's no easy way
  to recognize a discontinuous change in a clock.

- Be paranoid when cleaning out ttstart.

- Fix some minor issues.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2019-01-16 15:33:15 +01:00
parent f5808867cf
commit 4195bae67f
4 changed files with 58 additions and 20 deletions

View File

@ -2699,9 +2699,7 @@ class fhandler_timerfd : public fhandler_base
HANDLE get_timerfd_handle (); HANDLE get_timerfd_handle ();
void fixup_after_fork_exec (bool); void fixup_after_exec ();
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_read (select_stuff *);
select_record *select_write (select_stuff *); select_record *select_write (select_stuff *);

View File

@ -27,10 +27,19 @@ fhandler_timerfd::get_proc_fd_name (char *buf)
return strcpy (buf, "anon_inode:[timerfd]"); 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 int
fhandler_timerfd::timerfd (clockid_t clock_id, int flags) 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) if (flags & TFD_NONBLOCK)
set_nonblocking (true); set_nonblocking (true);
if (flags & TFD_CLOEXEC) if (flags & TFD_CLOEXEC)
@ -150,13 +159,9 @@ fhandler_timerfd::dup (fhandler_base *child, int flags)
} }
void void
fhandler_timerfd::fixup_after_fork_exec (bool execing) fhandler_timerfd::fixup_after_exec ()
{ {
if (!execing) if (!close_on_exec ())
{
/* TODO after fork */
}
else if (!close_on_exec ())
{ {
/* TODO after exec */ /* TODO after exec */
} }

View File

@ -22,7 +22,8 @@ details. */
#define EVENT_ARMED -1 #define EVENT_ARMED -1
#define EVENT_LOCK 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 class lock_timer_tracker
{ {
@ -79,7 +80,8 @@ timer_tracker::~timer_tracker ()
/* fd is true for timerfd timers. */ /* fd is true for timerfd timers. */
timer_tracker::timer_tracker (clockid_t c, const sigevent *e, bool fd) timer_tracker::timer_tracker (clockid_t c, const sigevent *e, bool fd)
: magic (TT_MAGIC), instance_count (1), clock_id (c), deleting (false), : 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) overrun_count_curr (0), overrun_count (0)
{ {
if (e != NULL) if (e != NULL)
@ -171,7 +173,7 @@ timer_tracker::disarm_event ()
LONG64 LONG64
timer_tracker::wait (bool nonblocking) timer_tracker::wait (bool nonblocking)
{ {
HANDLE w4[3] = { NULL, hcancel, timerfd_event }; HANDLE w4[3] = { NULL, hcancel, get_timerfd_handle () };
LONG64 ret = -1; LONG64 ret = -1;
wait_signal_arrived here (w4[0]); wait_signal_arrived here (w4[0]);
@ -264,7 +266,11 @@ timer_tracker::thread_func ()
{ {
if (!timerfd_event) if (!timerfd_event)
break; break;
arm_event (); if (arm_event ())
{
debug_printf ("%p timerfd already queued", this);
break;
}
SetEvent (timerfd_event); SetEvent (timerfd_event);
break; break;
} }
@ -443,21 +449,42 @@ timer_tracker::close (timer_tracker *tt)
return ret; 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 void
timer_tracker::fixup_after_fork () timer_tracker::fixup_after_fork ()
{ {
/* TODO: Keep timerfd timers available and restart them */
ttstart.hcancel = ttstart.syncthread = NULL; ttstart.hcancel = ttstart.syncthread = NULL;
ttstart.interval_us = ttstart.sleepto_us = 0;
ttstart.event_running = EVENT_DISARMED; ttstart.event_running = EVENT_DISARMED;
ttstart.overrun_count_curr = 0; ttstart.overrun_count_curr = ttstart.overrun_count = 0;
ttstart.overrun_count = 0;
for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */) for (timer_tracker *tt = &ttstart; tt->next != NULL; /* nothing */)
{ {
timer_tracker *deleteme = tt->next; timer_tracker *deleteme = tt->next;
if (deleteme->get_timerfd_handle ())
{
tt = deleteme;
tt->restart ();
}
else
{
tt->next = deleteme->next; tt->next = deleteme->next;
deleteme->hcancel = deleteme->syncthread = NULL; deleteme->timerfd_event = NULL;
deleteme->hcancel = NULL;
deleteme->syncthread = NULL;
delete deleteme; delete deleteme;
} }
}
} }
void void
@ -731,6 +758,8 @@ extern "C" int
timerfd_settime (int fd_in, int flags, const struct itimerspec *value, timerfd_settime (int fd_in, int flags, const struct itimerspec *value,
struct itimerspec *ovalue) 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) if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
{ {
set_errno (EINVAL); set_errno (EINVAL);

View File

@ -33,8 +33,14 @@ class timer_tracker
LONG decrement_instances (); LONG decrement_instances ();
int clean_and_unhook (); int clean_and_unhook ();
LONG64 _disarm_event (); LONG64 _disarm_event ();
void restart ();
public: 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 *, bool);
~timer_tracker (); ~timer_tracker ();
inline bool is_timer_tracker () const { return magic == TT_MAGIC; } inline bool is_timer_tracker () const { return magic == TT_MAGIC; }