/** * Schedule the timer t to fire at the current time plus a delay of <b>tv</b>. * All times are relative to tor_gettimeofday_cached_monotonic. */ void timer_schedule(tor_timer_t *t, const struct timeval *tv) { const timeout_t when = tv_to_timeout(tv); struct timeval now; tor_gettimeofday_cached_monotonic(&now); timer_advance_to_cur_time(&now); /* Take the old timeout value. */ timeout_t to = timeouts_timeout(global_timeouts); timeouts_add(global_timeouts, t, when); /* Should we update the libevent timer? */ if (to <= when) { return; /* we're already going to fire before this timer would trigger. */ } libevent_timer_reschedule(); }
static int check_randomized(const struct rand_cfg *cfg) { #define FAIL() do { \ printf("Failure on line %d\n", __LINE__); \ goto done; \ } while (0) int i, err; int rv = 1; struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout)); timeout_t *timeouts = calloc(cfg->n_timeouts, sizeof(timeout_t)); uint8_t *fired = calloc(cfg->n_timeouts, sizeof(uint8_t)); uint8_t *found = calloc(cfg->n_timeouts, sizeof(uint8_t)); uint8_t *deleted = calloc(cfg->n_timeouts, sizeof(uint8_t)); struct timeouts *tos = timeouts_open(0, &err); timeout_t now = cfg->start_at; int n_added_pending = 0, cnt_added_pending = 0; int n_added_expired = 0, cnt_added_expired = 0; struct timeouts_it it_p, it_e, it_all; int p_done = 0, e_done = 0, all_done = 0; struct timeout *to = NULL; const int rel = cfg->relative; if (!t || !timeouts || !tos || !fired || !found || !deleted) FAIL(); timeouts_update(tos, cfg->start_at); for (i = 0; i < cfg->n_timeouts; ++i) { if (&t[i] != timeout_init(&t[i], rel ? 0 : TIMEOUT_ABS)) FAIL(); if (timeout_pending(&t[i])) FAIL(); if (timeout_expired(&t[i])) FAIL(); timeouts[i] = random_to(cfg->min_timeout, cfg->max_timeout); timeouts_add(tos, &t[i], timeouts[i] - (rel ? now : 0)); if (timeouts[i] <= cfg->start_at) { if (timeout_pending(&t[i])) FAIL(); if (! timeout_expired(&t[i])) FAIL(); ++n_added_expired; } else { if (! timeout_pending(&t[i])) FAIL(); if (timeout_expired(&t[i])) FAIL(); ++n_added_pending; } } if (!!n_added_pending != timeouts_pending(tos)) FAIL(); if (!!n_added_expired != timeouts_expired(tos)) FAIL(); /* Test foreach, interleaving a few iterators. */ TIMEOUTS_IT_INIT(&it_p, TIMEOUTS_PENDING); TIMEOUTS_IT_INIT(&it_e, TIMEOUTS_EXPIRED); TIMEOUTS_IT_INIT(&it_all, TIMEOUTS_ALL); while (! (p_done && e_done && all_done)) { if (!p_done) { to = timeouts_next(tos, &it_p); if (to) { i = to - &t[0]; ++found[i]; ++cnt_added_pending; } else { p_done = 1; } } if (!e_done) { to = timeouts_next(tos, &it_e); if (to) { i = to - &t[0]; ++found[i]; ++cnt_added_expired; } else { e_done = 1; } } if (!all_done) { to = timeouts_next(tos, &it_all); if (to) { i = to - &t[0]; ++found[i]; } else { all_done = 1; } } } for (i = 0; i < cfg->n_timeouts; ++i) { if (found[i] != 2) FAIL(); } if (cnt_added_expired != n_added_expired) FAIL(); if (cnt_added_pending != n_added_pending) FAIL(); while (NULL != (to = timeouts_get(tos))) { i = to - &t[0]; assert(&t[i] == to); if (timeouts[i] > cfg->start_at) FAIL(); /* shouldn't have happened yet */ --n_added_expired; /* drop expired timeouts. */ ++fired[i]; } if (n_added_expired != 0) FAIL(); while (now < cfg->end_at) { int n_fired_this_time = 0; timeout_t first_at = timeouts_timeout(tos) + now; timeout_t oldtime = now; timeout_t step = random_to(1, cfg->max_step); int another; now += step; if (rel) timeouts_step(tos, step); else timeouts_update(tos, now); for (i = 0; i < cfg->try_removing; ++i) { int idx = random() % cfg->n_timeouts; if (! fired[idx]) { timeout_del(&t[idx]); ++deleted[idx]; } } another = (timeouts_timeout(tos) == 0); while (NULL != (to = timeouts_get(tos))) { if (! another) FAIL(); /* Thought we saw the last one! */ i = to - &t[0]; assert(&t[i] == to); if (timeouts[i] > now) FAIL(); /* shouldn't have happened yet */ if (timeouts[i] <= oldtime) FAIL(); /* should have happened already */ if (timeouts[i] < first_at) FAIL(); /* first_at should've been earlier */ fired[i]++; n_fired_this_time++; another = (timeouts_timeout(tos) == 0); } if (n_fired_this_time && first_at > now) FAIL(); /* first_at should've been earlier */ if (another) FAIL(); /* Huh? We think there are more? */ if (!timeouts_check(tos, stderr)) FAIL(); } for (i = 0; i < cfg->n_timeouts; ++i) { if (fired[i] > 1) FAIL(); /* Nothing fired twice. */ if (timeouts[i] <= now) { if (!(fired[i] || deleted[i])) FAIL(); } else { if (fired[i]) FAIL(); } if (fired[i] && deleted[i]) FAIL(); if (cfg->finalize > 1) { if (!fired[i]) timeout_del(&t[i]); } } /* Now nothing more should fire between now and the end of time. */ if (cfg->finalize) { timeouts_update(tos, THE_END_OF_TIME); if (cfg->finalize > 1) { if (timeouts_get(tos)) FAIL(); TIMEOUTS_FOREACH(to, tos, TIMEOUTS_ALL) FAIL(); } } rv = 0; done: if (tos) timeouts_close(tos); if (t) free(t); if (timeouts) free(timeouts); if (fired) free(fired); if (found) free(found); if (deleted) free(deleted); return rv; }
int check_intervals(struct intervals_cfg *cfg) { int i, err; int rv = 1; struct timeout *to; struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout)); unsigned *fired = calloc(cfg->n_timeouts, sizeof(unsigned)); struct timeouts *tos = timeouts_open(0, &err); timeout_t now = cfg->start_at; if (!t || !tos || !fired) FAIL(); timeouts_update(tos, now); for (i = 0; i < cfg->n_timeouts; ++i) { if (&t[i] != timeout_init(&t[i], TIMEOUT_INT)) FAIL(); if (timeout_pending(&t[i])) FAIL(); if (timeout_expired(&t[i])) FAIL(); timeouts_add(tos, &t[i], cfg->timeouts[i]); if (! timeout_pending(&t[i])) FAIL(); if (timeout_expired(&t[i])) FAIL(); } while (now < cfg->end_at) { timeout_t delay = timeouts_timeout(tos); if (cfg->skip && delay < cfg->skip) delay = cfg->skip; timeouts_step(tos, delay); now += delay; while (NULL != (to = timeouts_get(tos))) { i = to - &t[0]; assert(&t[i] == to); fired[i]++; if (0 != (to->expires - cfg->start_at) % cfg->timeouts[i]) FAIL(); if (to->expires <= now) FAIL(); if (to->expires > now + cfg->timeouts[i]) FAIL(); } if (!timeouts_check(tos, stderr)) FAIL(); } timeout_t duration = now - cfg->start_at; for (i = 0; i < cfg->n_timeouts; ++i) { if (cfg->skip) { if (fired[i] > duration / cfg->timeouts[i]) FAIL(); } else { if (fired[i] != duration / cfg->timeouts[i]) FAIL(); } if (!timeout_pending(&t[i])) FAIL(); } rv = 0; done: if (t) free(t); if (fired) free(fired); if (tos) free(tos); return rv; }