--- libev/ev.c 2019/12/20 05:20:50 1.514 +++ libev/ev.c 2019/12/20 20:51:46 1.515 @@ -180,6 +180,15 @@ # define EV_USE_EVENTFD 0 # endif +# if HAVE_SYS_TIMERFD_H +# ifndef EV_USE_TIMERFD +# define EV_USE_TIMERFD EV_FEATURE_OS +# endif +# else +# undef EV_USE_TIMERFD +# define EV_USE_TIMERFD 0 +# endif + #endif /* OS X, in its infinite idiocy, actually HARDCODES @@ -383,6 +392,14 @@ # endif #endif +#ifndef EV_USE_TIMERFD +# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)) +# define EV_USE_TIMERFD EV_FEATURE_OS +# else +# define EV_USE_TIMERFD 0 +# endif +#endif + #if 0 /* debugging */ # define EV_VERIFY 3 # define EV_USE_4HEAP 1 @@ -500,7 +517,7 @@ #endif #if EV_USE_EVENTFD -/* our minimum requirement is glibc 2.7 which has the stub, but not the header */ +/* our minimum requirement is glibc 2.7 which has the stub, but not the full header */ # include # ifndef EFD_NONBLOCK # define EFD_NONBLOCK O_NONBLOCK @@ -516,7 +533,7 @@ #endif #if EV_USE_SIGNALFD -/* our minimum requirement is glibc 2.7 which has the stub, but not the header */ +/* our minimum requirement is glibc 2.7 which has the stub, but not the full header */ # include # ifndef SFD_NONBLOCK # define SFD_NONBLOCK O_NONBLOCK @@ -528,7 +545,7 @@ # define SFD_CLOEXEC 02000000 # endif # endif -EV_CPP (extern "C") int signalfd (int fd, const sigset_t *mask, int flags); +EV_CPP (extern "C") int (signalfd) (int fd, const sigset_t *mask, int flags); struct signalfd_siginfo { @@ -537,6 +554,16 @@ }; #endif +/* for timerfd, libev core requires TFD_TIMER_CANCEL_ON_SET &c */ +#if EV_USE_TIMERFD +# include +/* timerfd is only used for periodics */ +# if !(defined (TFD_TIMER_CANCEL_ON_SET) && defined (TFD_CLOEXEC) && defined (TFD_NONBLOCK)) || !EV_PERIODIC_ENABLE +# undef EV_USE_TIMERFD +# define EV_USE_TIMERFD 0 +# endif +#endif + /*****************************************************************************/ #if EV_VERIFY >= 3 @@ -2850,6 +2877,58 @@ /*****************************************************************************/ +#if EV_USE_TIMERFD + +static void periodics_reschedule (EV_P); + +static void +timerfdcb (EV_P_ ev_io *iow, int revents) +{ + struct itimerspec its = { 0 }; + + /* since we can't easily come zup with a (portable) maximum value of time_t, + * we wake up once per month, which hopefully is rare enough to not + * be a problem. */ + its.it_value.tv_sec = ev_rt_now + 86400 * 30; + timerfd_settime (timerfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, 0); + + ev_rt_now = ev_time (); + /* periodics_reschedule only needs ev_rt_now */ + /* but maybe in the future we want the full treatment. */ + /* + now_floor = EV_TS_CONST (0.); + time_update (EV_A_ EV_TSTAMP_HUGE); + */ + periodics_reschedule (EV_A); +} + +ecb_noinline ecb_cold +static void +evtimerfd_init (EV_P) +{ + if (!ev_is_active (&timerfd_w)) + { + timerfd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + + if (timerfd >= 0) + { + fd_intern (timerfd); /* just to be sure */ + + ev_io_init (&timerfd_w, timerfdcb, timerfd, EV_READ); + ev_set_priority (&sigfd_w, EV_MINPRI); + ev_io_start (EV_A_ &timerfd_w); + ev_unref (EV_A); /* watcher should not keep loop alive */ + + /* (re-) arm timer */ + timerfdcb (EV_A_ 0, 0); + } + } +} + +#endif + +/*****************************************************************************/ + #if EV_USE_IOCP # include "ev_iocp.c" #endif @@ -3091,6 +3170,9 @@ #if EV_USE_SIGNALFD sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1; #endif +#if EV_USE_TIMERFD + timerfd = flags & EVFLAG_NOTIMERFD ? -1 : -2; +#endif if (!(flags & EVBACKEND_MASK)) flags |= ev_recommended_backends (); @@ -3173,6 +3255,11 @@ close (sigfd); #endif +#if EV_USE_TIMERFD + if (ev_is_active (&timerfd_w)) + close (timerfd); +#endif + #if EV_USE_INOTIFY if (fs_fd >= 0) close (fs_fd); @@ -3273,22 +3360,44 @@ infy_fork (EV_A); #endif -#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE - if (ev_is_active (&pipe_w) && postfork != 2) + if (postfork != 2) { - /* pipe_write_wanted must be false now, so modifying fd vars should be safe */ - - ev_ref (EV_A); - ev_io_stop (EV_A_ &pipe_w); - - if (evpipe [0] >= 0) - EV_WIN32_CLOSE_FD (evpipe [0]); + #if EV_USE_SIGNALFD + /* surprisingly, nothing needs to be done for signalfd, accoridng to docs, it does the right thing on fork */ + #endif + + #if EV_USE_TIMERFD + if (ev_is_active (&timerfd_w)) + { + ev_ref (EV_A); + ev_io_stop (EV_A_ &timerfd_w); - evpipe_init (EV_A); - /* iterate over everything, in case we missed something before */ - ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM); + close (timerfd); + timerfd = -2; + + evtimerfd_init (EV_A); + /* reschedule periodics, in case we missed something */ + ev_feed_event (EV_A_ &timerfd_w, EV_CUSTOM); + } + #endif + + #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE + if (ev_is_active (&pipe_w)) + { + /* pipe_write_wanted must be false now, so modifying fd vars should be safe */ + + ev_ref (EV_A); + ev_io_stop (EV_A_ &pipe_w); + + if (evpipe [0] >= 0) + EV_WIN32_CLOSE_FD (evpipe [0]); + + evpipe_init (EV_A); + /* iterate over everything, in case we missed something before */ + ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM); + } + #endif } -#endif postfork = 0; } @@ -4214,6 +4323,11 @@ if (ecb_expect_false (ev_is_active (w))) return; +#if EV_USE_TIMERFD + if (timerfd == -2) + evtimerfd_init (EV_A); +#endif + if (w->reschedule_cb) ev_at (w) = w->reschedule_cb (w, ev_rt_now); else if (w->interval)