mirror of /home/gitosis/repositories/libev.git
new floor(), better periodic recalc
This commit is contained in:
parent
4a748881d0
commit
ed4a1b5f25
2
Changes
2
Changes
|
@ -3,7 +3,7 @@ Revision history for libev, a high-performance and full-featured event loop.
|
|||
TODO: ev_loop_prefork hint?
|
||||
|
||||
- change the default periodic reschedule function to hopefully be more
|
||||
exact and correct.
|
||||
exact and correct even in corner cases or in the far future.
|
||||
- document reasonable ranges for interval and offset.
|
||||
- do not rely on -lm anymore: use it when available but use our
|
||||
own floor () if it is missing. This should make it easier to embed.
|
||||
|
|
111
ev.c
111
ev.c
|
@ -45,6 +45,12 @@
|
|||
# include "config.h"
|
||||
# endif
|
||||
|
||||
#if HAVE_FLOOR
|
||||
# ifndef EV_USE_FLOOR
|
||||
# define EV_USE_FLOOR 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if HAVE_CLOCK_SYSCALL
|
||||
# ifndef EV_USE_CLOCK_SYSCALL
|
||||
# define EV_USE_CLOCK_SYSCALL 1
|
||||
|
@ -158,7 +164,6 @@
|
|||
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -234,6 +239,10 @@ EV_CPP(extern "C" {)
|
|||
# define EV_NSIG 65
|
||||
#endif
|
||||
|
||||
#ifndef EV_USE_FLOOR
|
||||
# define EV_USE_FLOOR 0
|
||||
#endif
|
||||
|
||||
#ifndef EV_USE_CLOCK_SYSCALL
|
||||
# if __linux && __GLIBC__ >= 2
|
||||
# define EV_USE_CLOCK_SYSCALL EV_FEATURE_OS
|
||||
|
@ -445,14 +454,11 @@ struct signalfd_siginfo
|
|||
#endif
|
||||
|
||||
/*
|
||||
* This is used to avoid floating point rounding problems.
|
||||
* It is added to ev_rt_now when scheduling periodics
|
||||
* to ensure progress, time-wise, even when rounding
|
||||
* errors are against us.
|
||||
* This is used to work around floating point rounding problems.
|
||||
* This value is good at least till the year 4000.
|
||||
* Better solutions welcome.
|
||||
*/
|
||||
#define TIME_EPSILON 0.0001220703125 /* 1/8192 */
|
||||
#define MIN_INTERVAL 0.0001220703125 /* 1/2**13, good till 4000 */
|
||||
/*#define MIN_INTERVAL 0.00000095367431640625 /* 1/2**20, good till 2200 */
|
||||
|
||||
#define MIN_TIMEJUMP 1. /* minimum timejump that gets detected (if monotonic clock available) */
|
||||
#define MAX_BLOCKTIME 59.743 /* never wait longer than this time (to detect time jumps) */
|
||||
|
@ -525,6 +531,54 @@ static EV_ATOMIC_T have_monotonic; /* did clock_gettime (CLOCK_MONOTONIC) work?
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* define a suitable floor function (only used by periodics atm) */
|
||||
|
||||
#if EV_USE_FLOOR
|
||||
# include <math.h>
|
||||
# define ev_floor(v) floor (v)
|
||||
#else
|
||||
|
||||
#include <float.h>
|
||||
|
||||
/* a floor() replacement function, should be independent of ev_tstamp type */
|
||||
static ev_tstamp noinline
|
||||
ev_floor (ev_tstamp v)
|
||||
{
|
||||
/* the choice of shift factor is not terribly important */
|
||||
#if FLT_RADIX != 2 /* assume FLT_RADIX == 10 */
|
||||
const ev_tstamp shift = sizeof (unsigned long) >= 8 ? 10000000000000000000. : 1000000000.;
|
||||
#else
|
||||
const ev_tstamp shift = sizeof (unsigned long) >= 8 ? 18446744073709551616. : 4294967296.;
|
||||
#endif
|
||||
|
||||
/* argument too large for an unsigned long? */
|
||||
if (expect_false (v >= shift))
|
||||
{
|
||||
ev_tstamp f;
|
||||
|
||||
if (v == v - 1.)
|
||||
return v; /* very large number */
|
||||
|
||||
f = shift * ev_floor (v * (1. / shift));
|
||||
return f + ev_floor (v - f);
|
||||
}
|
||||
|
||||
/* special treatment for negative args? */
|
||||
if (expect_false (v < 0.))
|
||||
{
|
||||
ev_tstamp f = -ev_floor (-v);
|
||||
|
||||
return f - (f == v ? 0 : 1);
|
||||
}
|
||||
|
||||
/* fits into an unsigned long */
|
||||
return (unsigned long)v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifdef __linux
|
||||
# include <sys/utsname.h>
|
||||
#endif
|
||||
|
@ -2210,12 +2264,28 @@ timers_reify (EV_P)
|
|||
|
||||
#if EV_PERIODIC_ENABLE
|
||||
|
||||
inline_speed void
|
||||
static void noinline
|
||||
periodic_recalc (EV_P_ ev_periodic *w)
|
||||
{
|
||||
/* TODO: use slow but potentially more correct incremental algo, */
|
||||
/* also do not rely on ceil */
|
||||
ev_at (w) = w->offset + ceil ((ev_rt_now - w->offset) / w->interval) * w->interval;
|
||||
ev_tstamp interval = w->interval > MIN_INTERVAL ? w->interval : MIN_INTERVAL;
|
||||
ev_tstamp at = w->offset + interval * ev_floor ((ev_rt_now - w->offset) / interval);
|
||||
|
||||
/* the above almost always errs on the low side */
|
||||
while (at <= ev_rt_now)
|
||||
{
|
||||
ev_tstamp nat = at + w->interval;
|
||||
|
||||
/* when resolution fails us, we use ev_rt_now */
|
||||
if (expect_false (nat == at))
|
||||
{
|
||||
at = ev_rt_now;
|
||||
break;
|
||||
}
|
||||
|
||||
at = nat;
|
||||
}
|
||||
|
||||
ev_at (w) = at;
|
||||
}
|
||||
|
||||
/* make periodics pending */
|
||||
|
@ -2247,20 +2317,6 @@ periodics_reify (EV_P)
|
|||
else if (w->interval)
|
||||
{
|
||||
periodic_recalc (EV_A_ w);
|
||||
|
||||
/* if next trigger time is not sufficiently in the future, put it there */
|
||||
/* this might happen because of floating point inexactness */
|
||||
if (ev_at (w) - ev_rt_now < TIME_EPSILON)
|
||||
{
|
||||
ev_at (w) += w->interval;
|
||||
|
||||
/* if interval is unreasonably low we might still have a time in the past */
|
||||
/* so correct this. this will make the periodic very inexact, but the user */
|
||||
/* has effectively asked to get triggered more often than possible */
|
||||
if (ev_at (w) < ev_rt_now)
|
||||
ev_at (w) = ev_rt_now;
|
||||
}
|
||||
|
||||
ANHE_at_cache (periodics [HEAP0]);
|
||||
downheap (periodics, periodiccnt, HEAP0);
|
||||
}
|
||||
|
@ -2348,9 +2404,12 @@ time_update (EV_P_ ev_tstamp max_block)
|
|||
*/
|
||||
for (i = 4; --i; )
|
||||
{
|
||||
ev_tstamp diff;
|
||||
rtmn_diff = ev_rt_now - mn_now;
|
||||
|
||||
if (expect_true (fabs (odiff - rtmn_diff) < MIN_TIMEJUMP))
|
||||
diff = odiff - rtmn_diff;
|
||||
|
||||
if (expect_true ((diff < 0. ? -diff : diff) < MIN_TIMEJUMP))
|
||||
return; /* all is well */
|
||||
|
||||
ev_rt_now = ev_time ();
|
||||
|
|
18
ev.pod
18
ev.pod
|
@ -2153,9 +2153,12 @@ Another way to think about it (for the mathematically inclined) is that
|
|||
C<ev_periodic> will try to run the callback in this mode at the next possible
|
||||
time where C<time = offset (mod interval)>, regardless of any time jumps.
|
||||
|
||||
For numerical stability it is preferable that the C<offset> value is near
|
||||
C<ev_now ()> (the current time), but there is no range requirement for
|
||||
this value, and in fact is often specified as zero.
|
||||
The C<interval> I<MUST> be positive, and for numerical stability, the
|
||||
interval value should be higher than C<1/8192> (which is around 100
|
||||
microseconds) and C<offset> should be higher than C<0> and should have
|
||||
at most a similar magnitude as the current time (say, within a factor of
|
||||
ten). Typical values for offset are, in fact, C<0> or something between
|
||||
C<0> and C<interval>, which is also the recommended range.
|
||||
|
||||
Note also that there is an upper limit to how often a timer can fire (CPU
|
||||
speed for example), so if C<interval> is very small then timing stability
|
||||
|
@ -4206,6 +4209,15 @@ F<event.h> that are not directly supported by the libev core alone.
|
|||
In standalone mode, libev will still try to automatically deduce the
|
||||
configuration, but has to be more conservative.
|
||||
|
||||
=item EV_USE_FLOOR
|
||||
|
||||
If defined to be C<1>, libev will use the C<floor ()> function for its
|
||||
periodic reschedule calculations, otherwise libev will fall back on a
|
||||
portable (slower) implementation. If you enable this, you usually have to
|
||||
link against libm or something equivalent. Enabling this when the C<floor>
|
||||
function is not available will fail, so the safe default is to not enable
|
||||
this.
|
||||
|
||||
=item EV_USE_MONOTONIC
|
||||
|
||||
If defined to be C<1>, libev will try to detect the availability of the
|
||||
|
|
7
libev.m4
7
libev.m4
|
@ -16,7 +16,7 @@ AC_CHECK_FUNCS(clock_gettime, [], [
|
|||
#include <time.h>],
|
||||
[struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])],
|
||||
[ac_have_clock_syscall=1
|
||||
AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, "use syscall interface for clock_gettime")
|
||||
AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime)
|
||||
AC_MSG_RESULT(yes)],
|
||||
[AC_MSG_RESULT(no)])
|
||||
fi
|
||||
|
@ -35,5 +35,8 @@ AC_CHECK_FUNCS(nanosleep, [], [
|
|||
fi
|
||||
])
|
||||
|
||||
AC_CHECK_LIB(m, ceil)
|
||||
if test -z "$LIBEV_M4_AVOID_LIBM"; then
|
||||
LIBM=m
|
||||
fi
|
||||
AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)])
|
||||
|
||||
|
|
Loading…
Reference in New Issue