summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Alexander Lehmann <libev@schmorp.de>2019-12-20 20:51:46 +0000
committerMarc Alexander Lehmann <libev@schmorp.de>2019-12-20 20:51:46 +0000
commit598407e8329cd00bf0953c66974fa70e7d79bea8 (patch)
treecf1eb7168daa3a9c63d0dc2f421516f6356cfb1d
parentebd88ad8a1effecb487257f41a71884390913f3e (diff)
downloadlibev-598407e8329cd00bf0953c66974fa70e7d79bea8.tar.gz
libev-598407e8329cd00bf0953c66974fa70e7d79bea8.zip
*** empty log message ***
-rw-r--r--Changes6
-rw-r--r--ev.332
-rw-r--r--ev.c146
-rw-r--r--ev.h15
-rw-r--r--ev.pod35
-rw-r--r--ev_vars.h5
-rw-r--r--ev_wrap.h4
7 files changed, 216 insertions, 27 deletions
diff --git a/Changes b/Changes
index 78be1b0..48e6ed2 100644
--- a/Changes
+++ b/Changes
@@ -5,9 +5,13 @@ TODO: maybe use timerfd to detect time jumps on linux
TODO: document EV_TSTAMP_T
4.31
+TODO: use TFD_TIMER_CANCEL_ON_SET to call periodics_reschedule, maybe? (must)
- handle backends with minimum wait time a bit better by not
waiting in the presence of already-expired timers
- (behaviour reported by Felipe Gsper).
+ (behaviour reported by Felipe Gasper).
+ - new feature: use timerfd to detect timejumps quickly,
+ can be disabled with the new EVFLAG_NOTIMERFD loop flag.
+ - document EV_USE_SIGNALFD feature macro.
4.30 (EV only)
- change non-autoconf test for __kernel_rwf_t by testing
diff --git a/ev.3 b/ev.3
index 8c06f47..060b7ce 100644
--- a/ev.3
+++ b/ev.3
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "LIBEV 3"
-.TH LIBEV 3 "2019-07-07" "libev-4.27" "libev - high performance full featured event loop"
+.TH LIBEV 3 "2019-12-20" "libev-4.27" "libev - high performance full featured event loop"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -595,6 +595,13 @@ threads that are not interested in handling them.
Signalfd will not be used by default as this changes your signal mask, and
there are a lot of shoddy libraries and programs (glib's threadpool for
example) that can't properly initialise their signal masks.
+.ie n .IP """EVFLAG_NOTIMERFD""" 4
+.el .IP "\f(CWEVFLAG_NOTIMERFD\fR" 4
+.IX Item "EVFLAG_NOTIMERFD"
+When this flag is specified, the libev will avoid using a \f(CW\*(C`timerfd\*(C'\fR to
+detect time jumps. It will still be able to detect time jumps, but takes
+longer and has a lower accuracy in doing so, but saves a file descriptor
+per loop.
.ie n .IP """EVFLAG_NOSIGMASK""" 4
.el .IP "\f(CWEVFLAG_NOSIGMASK\fR" 4
.IX Item "EVFLAG_NOSIGMASK"
@@ -1656,7 +1663,7 @@ Many event loops support \fIwatcher priorities\fR, which are usually small
integers that influence the ordering of event callback invocation
between watchers in some way, all else being equal.
.PP
-In libev, Watcher priorities can be set using \f(CW\*(C`ev_set_priority\*(C'\fR. See its
+In libev, watcher priorities can be set using \f(CW\*(C`ev_set_priority\*(C'\fR. See its
description for the more technical details such as the actual priority
range.
.PP
@@ -4756,6 +4763,27 @@ available and will probe for kernel support at runtime. This will improve
\&\f(CW\*(C`ev_signal\*(C'\fR and \f(CW\*(C`ev_async\*(C'\fR performance and reduce resource consumption.
If undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
2.7 or newer, otherwise disabled.
+.IP "\s-1EV_USE_SIGNALFD\s0" 4
+.IX Item "EV_USE_SIGNALFD"
+If defined to be \f(CW1\fR, then libev will assume that \f(CW\*(C`signalfd ()\*(C'\fR is
+available and will probe for kernel support at runtime. This enables
+the use of \s-1EVFLAG_SIGNALFD\s0 for faster and simpler signal handling. If
+undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
+2.7 or newer, otherwise disabled.
+.IP "\s-1EV_USE_TIMERFD\s0" 4
+.IX Item "EV_USE_TIMERFD"
+If defined to be \f(CW1\fR, then libev will assume that \f(CW\*(C`timerfd ()\*(C'\fR is
+available and will probe for kernel support at runtime. This allows
+libev to detect time jumps accurately. If undefined, it will be enabled
+if the headers indicate GNU/Linux + Glibc 2.8 or newer and define
+\&\f(CW\*(C`TFD_TIMER_CANCEL_ON_SET\*(C'\fR, otherwise disabled.
+.IP "\s-1EV_USE_EVENTFD\s0" 4
+.IX Item "EV_USE_EVENTFD"
+If defined to be \f(CW1\fR, then libev will assume that \f(CW\*(C`eventfd ()\*(C'\fR is
+available and will probe for kernel support at runtime. This will improve
+\&\f(CW\*(C`ev_signal\*(C'\fR and \f(CW\*(C`ev_async\*(C'\fR performance and reduce resource consumption.
+If undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
+2.7 or newer, otherwise disabled.
.IP "\s-1EV_USE_SELECT\s0" 4
.IX Item "EV_USE_SELECT"
If undefined or defined to be \f(CW1\fR, libev will compile in support for the
diff --git a/ev.c b/ev.c
index c769735..5f68687 100644
--- a/ev.c
+++ b/ev.c
@@ -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 <stdint.h>
# ifndef EFD_NONBLOCK
# define EFD_NONBLOCK O_NONBLOCK
@@ -516,7 +533,7 @@ EV_CPP(extern "C") int (eventfd) (unsigned int initval, int flags);
#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 <stdint.h>
# ifndef SFD_NONBLOCK
# define SFD_NONBLOCK O_NONBLOCK
@@ -528,7 +545,7 @@ EV_CPP(extern "C") int (eventfd) (unsigned int initval, int flags);
# 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 @@ struct signalfd_siginfo
};
#endif
+/* for timerfd, libev core requires TFD_TIMER_CANCEL_ON_SET &c */
+#if EV_USE_TIMERFD
+# include <sys/timerfd.h>
+/* 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 @@ childcb (EV_P_ ev_signal *sw, int revents)
/*****************************************************************************/
+#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 @@ loop_init (EV_P_ unsigned int flags) EV_NOEXCEPT
#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 @@ ev_loop_destroy (EV_P)
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 @@ loop_fork (EV_P)
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 @@ ev_periodic_start (EV_P_ ev_periodic *w) EV_NOEXCEPT
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)
diff --git a/ev.h b/ev.h
index 7966051..dfb82b3 100644
--- a/ev.h
+++ b/ev.h
@@ -504,17 +504,18 @@ union ev_any_watcher
/* flag bits for ev_default_loop and ev_loop_new */
enum {
/* the default */
- EVFLAG_AUTO = 0x00000000U, /* not quite a mask */
+ EVFLAG_AUTO = 0x00000000U, /* not quite a mask */
/* flag bits */
- EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */
- EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */
+ EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */
+ EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */
/* debugging/feature disable */
- EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */
+ EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */
#if EV_COMPAT3
- EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */
+ EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */
#endif
- EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */
- EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */
+ EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */
+ EVFLAG_NOSIGMASK = 0x00400000U, /* avoid modifying the signal mask */
+ EVFLAG_NOTIMERFD = 0x00800000U /* avoid creating a timerfd */
};
/* method bits to be ored together */
diff --git a/ev.pod b/ev.pod
index fc8cdce..5d60ed2 100644
--- a/ev.pod
+++ b/ev.pod
@@ -482,7 +482,16 @@ unblocking the signals.
It's also required by POSIX in a threaded program, as libev calls
C<sigprocmask>, whose behaviour is officially unspecified.
-This flag's behaviour will become the default in future versions of libev.
+=item C<EVFLAG_NOTIMERFD>
+
+When this flag is specified, the libev will avoid using a C<timerfd> to
+detect time jumps. It will still be able to detect time jumps, but takes
+longer and has a lower accuracy in doing so, but saves a file descriptor
+per loop.
+
+The current implementation only tries to use a C<timerfd> when the first
+C<ev_periodic> watcher is started and falls back on other methods if it
+cannot be created, but this behaviour might change in the future.
=item C<EVBACKEND_SELECT> (value 1, portable select backend)
@@ -4617,6 +4626,30 @@ C<ev_signal> and C<ev_async> performance and reduce resource consumption.
If undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
2.7 or newer, otherwise disabled.
+=item EV_USE_SIGNALFD
+
+If defined to be C<1>, then libev will assume that C<signalfd ()> is
+available and will probe for kernel support at runtime. This enables
+the use of EVFLAG_SIGNALFD for faster and simpler signal handling. If
+undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
+2.7 or newer, otherwise disabled.
+
+=item EV_USE_TIMERFD
+
+If defined to be C<1>, then libev will assume that C<timerfd ()> is
+available and will probe for kernel support at runtime. This allows
+libev to detect time jumps accurately. If undefined, it will be enabled
+if the headers indicate GNU/Linux + Glibc 2.8 or newer and define
+C<TFD_TIMER_CANCEL_ON_SET>, otherwise disabled.
+
+=item EV_USE_EVENTFD
+
+If defined to be C<1>, then libev will assume that C<eventfd ()> is
+available and will probe for kernel support at runtime. This will improve
+C<ev_signal> and C<ev_async> performance and reduce resource consumption.
+If undefined, it will be enabled if the headers indicate GNU/Linux + Glibc
+2.7 or newer, otherwise disabled.
+
=item EV_USE_SELECT
If undefined or defined to be C<1>, libev will compile in support for the
diff --git a/ev_vars.h b/ev_vars.h
index 44b5bbd..4a56321 100644
--- a/ev_vars.h
+++ b/ev_vars.h
@@ -228,6 +228,11 @@ VARx(ev_io, sigfd_w)
VARx(sigset_t, sigfd_set)
#endif
+#if EV_USE_TIMERFD || EV_GENWRAP
+VARx(int, timerfd) /* timerfd for time jump detection */
+VARx(ev_io, timerfd_w)
+#endif
+
VARx(unsigned int, origflags) /* original loop flags */
#if EV_FEATURE_API || EV_GENWRAP
diff --git a/ev_wrap.h b/ev_wrap.h
index e6b7cb3..41bba7a 100644
--- a/ev_wrap.h
+++ b/ev_wrap.h
@@ -124,6 +124,8 @@
#define sigfd_w ((loop)->sigfd_w)
#define timeout_blocktime ((loop)->timeout_blocktime)
#define timercnt ((loop)->timercnt)
+#define timerfd ((loop)->timerfd)
+#define timerfd_w ((loop)->timerfd_w)
#define timermax ((loop)->timermax)
#define timers ((loop)->timers)
#define userdata ((loop)->userdata)
@@ -258,6 +260,8 @@
#undef sigfd_w
#undef timeout_blocktime
#undef timercnt
+#undef timerfd
+#undef timerfd_w
#undef timermax
#undef timers
#undef userdata