summaryrefslogtreecommitdiff
path: root/ev.c
diff options
context:
space:
mode:
authorroot <root>2011-02-20 02:56:23 +0000
committerroot <root>2011-02-20 02:56:23 +0000
commit9b90eb0a8ba0ae6348fd0574f6382f5728e200d8 (patch)
treeee72d474cfee9634018e5a2c0e67ceed05ba42d3 /ev.c
parent8d3dfc6c8f54e355abc6c0a272a054365fbdb2a5 (diff)
new floor(), better periodic recalc
Diffstat (limited to 'ev.c')
-rw-r--r--ev.c111
1 files changed, 85 insertions, 26 deletions
diff --git a/ev.c b/ev.c
index 4187b18..dc737b2 100644
--- a/ev.c
+++ b/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 ();