X-Git-Url: https://git.llucax.com/software/libev.git/blobdiff_plain/759051fdbf8b3370705e05dac2e2d7525d882e4c..4ce77a5b1de05dae6092eae478b2a4397173d9c3:/ev.c diff --git a/ev.c b/ev.c index 3fc528a..acde504 100644 --- a/ev.c +++ b/ev.c @@ -218,9 +218,19 @@ extern "C" { /**/ +/* + * 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 value is good at least till the year 4000. + * Better solutions welcome. + */ +#define TIME_EPSILON 0.0001220703125 /* 1/8192 */ + #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) */ -/*#define CLEANUP_INTERVAL (MAX_BLOCKTIME * 5.) /* how often to try to free memory and re-check fds */ +/*#define CLEANUP_INTERVAL (MAX_BLOCKTIME * 5.) /* how often to try to free memory and re-check fds, TODO */ #if __GNUC__ >= 3 # define expect(expr,value) __builtin_expect ((expr),(value)) @@ -1233,13 +1243,14 @@ periodics_reify (EV_P) /* first reschedule or stop timer */ if (w->reschedule_cb) { - ((WT)w)->at = w->reschedule_cb (w, ev_rt_now + 0.0001); + ((WT)w)->at = w->reschedule_cb (w, ev_rt_now + TIME_EPSILON); assert (("ev_periodic reschedule callback returned time in the past", ((WT)w)->at > ev_rt_now)); downheap ((WT *)periodics, periodiccnt, 0); } else if (w->interval) { - ((WT)w)->at = w->offset + floor ((ev_rt_now - w->offset) / w->interval + 1.) * w->interval; + ((WT)w)->at = w->offset + ceil ((ev_rt_now - w->offset) / w->interval) * w->interval; + if (((WT)w)->at - ev_rt_now <= TIME_EPSILON) ((WT)w)->at += w->interval; assert (("ev_periodic timeout in the past detected while processing timers, negative interval?", ((WT)w)->at > ev_rt_now)); downheap ((WT *)periodics, periodiccnt, 0); } @@ -1295,74 +1306,65 @@ idle_reify (EV_P) } #endif -int inline_size -time_update_monotonic (EV_P) -{ - mn_now = get_clock (); - - if (expect_true (mn_now - now_floor < MIN_TIMEJUMP * .5)) - { - ev_rt_now = rtmn_diff + mn_now; - return 0; - } - else - { - now_floor = mn_now; - ev_rt_now = ev_time (); - return 1; - } -} - -void inline_size -time_update (EV_P) +void inline_speed +time_update (EV_P_ ev_tstamp max_block) { int i; #if EV_USE_MONOTONIC if (expect_true (have_monotonic)) { - if (time_update_monotonic (EV_A)) + ev_tstamp odiff = rtmn_diff; + + mn_now = get_clock (); + + /* only fetch the realtime clock every 0.5*MIN_TIMEJUMP seconds */ + /* interpolate in the meantime */ + if (expect_true (mn_now - now_floor < MIN_TIMEJUMP * .5)) { - ev_tstamp odiff = rtmn_diff; - - /* loop a few times, before making important decisions. - * on the choice of "4": one iteration isn't enough, - * in case we get preempted during the calls to - * ev_time and get_clock. a second call is almost guaranteed - * to succeed in that case, though. and looping a few more times - * doesn't hurt either as we only do this on time-jumps or - * in the unlikely event of having been preempted here. - */ - for (i = 4; --i; ) - { - rtmn_diff = ev_rt_now - mn_now; + ev_rt_now = rtmn_diff + mn_now; + return; + } + + now_floor = mn_now; + ev_rt_now = ev_time (); - if (fabs (odiff - rtmn_diff) < MIN_TIMEJUMP) - return; /* all is well */ + /* loop a few times, before making important decisions. + * on the choice of "4": one iteration isn't enough, + * in case we get preempted during the calls to + * ev_time and get_clock. a second call is almost guaranteed + * to succeed in that case, though. and looping a few more times + * doesn't hurt either as we only do this on time-jumps or + * in the unlikely event of having been preempted here. + */ + for (i = 4; --i; ) + { + rtmn_diff = ev_rt_now - mn_now; - ev_rt_now = ev_time (); - mn_now = get_clock (); - now_floor = mn_now; - } + if (fabs (odiff - rtmn_diff) < MIN_TIMEJUMP) + return; /* all is well */ + + ev_rt_now = ev_time (); + mn_now = get_clock (); + now_floor = mn_now; + } # if EV_PERIODIC_ENABLE - periodics_reschedule (EV_A); + periodics_reschedule (EV_A); # endif - /* no timer adjustment, as the monotonic clock doesn't jump */ - /* timers_reschedule (EV_A_ rtmn_diff - odiff) */ - } + /* no timer adjustment, as the monotonic clock doesn't jump */ + /* timers_reschedule (EV_A_ rtmn_diff - odiff) */ } else #endif { ev_rt_now = ev_time (); - if (expect_false (mn_now > ev_rt_now || mn_now < ev_rt_now - MAX_BLOCKTIME - MIN_TIMEJUMP)) + if (expect_false (mn_now > ev_rt_now || ev_rt_now > mn_now + max_block + MIN_TIMEJUMP)) { #if EV_PERIODIC_ENABLE periodics_reschedule (EV_A); #endif - /* adjust timers. this is easy, as the offset is the same for all of them */ for (i = 0; i < timercnt; ++i) ((WT)timers [i])->at += ev_rt_now - mn_now; @@ -1442,15 +1444,7 @@ ev_loop (EV_P_ int flags) else { /* update time to cancel out callback processing overhead */ -#if EV_USE_MONOTONIC - if (expect_true (have_monotonic)) - time_update_monotonic (EV_A); - else -#endif - { - ev_rt_now = ev_time (); - mn_now = ev_rt_now; - } + time_update (EV_A_ 1e100); block = MAX_BLOCKTIME; @@ -1473,10 +1467,10 @@ ev_loop (EV_P_ int flags) ++loop_count; backend_poll (EV_A_ block); - } - /* update ev_rt_now, do magic */ - time_update (EV_A); + /* update ev_rt_now, do magic */ + time_update (EV_A_ block); + } /* queue pending timers and reschedule them */ timers_reify (EV_A); /* relative timers called last */