/** * Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. * Copyright (C) UT-Battelle, LLC. 2014. ALL RIGHTS RESERVED. * See file LICENSE for terms. */ #include extern "C" { #include } #include /** * note: the fast timer precision is dependent on context switch latency!!! * expected timer precision 2x wheel resolution plus some time for processing and * context switching */ class twheel : public ucs::test { protected: struct hr_timer { ucs_wtimer_t timer; int tid; ucs_time_t start_time; ucs_time_t end_time; ucs_time_t d; ucs_time_t total_time; twheel *self; }; ucs_twheel_t m_wheel; // @override virtual void init(); // @override virtual void cleanup(); static void timer_func(ucs_wtimer_t *self); static bool check_all_timers_triggered(std::vector &t); void timer_expired(struct hr_timer *t); void add_timer(struct hr_timer *t); void init_timer(struct hr_timer *t, int id); void init_timerv(struct hr_timer *v, int n); void set_timer_delta(struct hr_timer *t, int how); }; void twheel::init() { ucs_twheel_init(&m_wheel, ucs_time_from_usec(32) * ucs::test_time_multiplier(), ucs_get_time()); } void twheel::cleanup() { ucs_twheel_cleanup(&m_wheel); } void twheel::timer_func(ucs_wtimer_t *self) { struct hr_timer *t = ucs_container_of(self, struct hr_timer, timer); t->self->timer_expired(t); } bool twheel::check_all_timers_triggered(std::vector &t) { for (int i = 0; i < t.size(); i++) { if (t[i].end_time == (ucs_time_t)0) { return false; } } return true; } void twheel::timer_expired(struct hr_timer *t) { t->total_time += (m_wheel.now - t->start_time); t->end_time = m_wheel.now; } void twheel::add_timer(struct hr_timer *t) { t->end_time = 0; ASSERT_EQ(ucs_wtimer_add(&m_wheel, &t->timer, t->d), UCS_OK); t->start_time = ucs_get_time(); } void twheel::init_timer(struct hr_timer *t, int id) { t->tid = id; t->total_time = 0; t->self = this; ucs_wtimer_init(&t->timer, timer_func); } void twheel::init_timerv(struct hr_timer *v, int n) { for (int i = 0; i < n; i++) { init_timer(&v[i], i); } } void twheel::set_timer_delta(struct hr_timer *t, int how) { int slot; switch (how) { case 0: /* first */ slot = 1; break; case 1: /* last */ slot = m_wheel.num_slots - 1; break; case 2: /* middle */ slot = m_wheel.num_slots / 2; break; case -2: /* overflow */ slot = m_wheel.num_slots + (ucs::rand() % 1000000); break; default: slot = 1 + ucs::rand() % (m_wheel.num_slots - 2); break; } if (how == -2) { t->d = m_wheel.res + m_wheel.res * (m_wheel.num_slots - 1) / 2; } else { t->d = m_wheel.res + m_wheel.res * slot / 2; } } #define N_LOOPS 20 UCS_TEST_SKIP_COND_F(twheel, precision_single, true) { // Test is broken #if 0 struct hr_timer t; ucs_time_t now; int i, k; int fail_count; init_timer(&t, 0); for (k = 0; k < 10; k++ ) { set_timer_delta(&t, k); fail_count = 0; for (i = 0; i < N_LOOPS; i++) { t.total_time = 0; add_timer(&t); do { now = ucs_get_time(); ucs_twheel_sweep(&m_wheel, now); } while (t.end_time == 0); if ((ucs_time_t)::abs(t.total_time - t.d) > 2 * m_wheel.res) { ++fail_count; } } EXPECT_LE(fail_count, N_LOOPS / 3); } #endif } #define N_TIMERS 10000 UCS_TEST_SKIP_COND_F(twheel, precision_multi, true) { // Test is broken #if 0 std::vector t(N_TIMERS); ucs_time_t start, now, eps; init_timerv(&t[0], N_TIMERS); for (int i = 0; i < N_TIMERS; i++) { set_timer_delta(&t[i], i); add_timer(&t[i]); } start = ucs_get_time(); /* all timers were delayed by at most eps */ eps = start - m_wheel.now; do { now = ucs_get_time(); ucs_twheel_sweep(&m_wheel, now); } while (now < start + m_wheel.res * m_wheel.num_slots); /* all timers should ve been triggered * correct delta */ for (int i = 0; i < N_TIMERS; i++) { EXPECT_NE(t[i].end_time, (ucs_time_t)0); EXPECT_NEAR(t[i].total_time, t[i].d, 2 * m_wheel.res + eps); } #endif } UCS_TEST_F(twheel, add_twice) { struct hr_timer t; init_timer(&t, 0); set_timer_delta(&t, -1); add_timer(&t); set_timer_delta(&t, -1); EXPECT_EQ(ucs_wtimer_add(&m_wheel, &t.timer, t.d), UCS_ERR_BUSY); do { ucs_twheel_sweep(&m_wheel, ucs_get_time()); /* coverity[loop_condition] */ } while(t.end_time == 0); } UCS_TEST_SKIP_COND_F(twheel, add_overflow, true) { // Test is broken #if 0 struct hr_timer t; init_timer(&t, 0); t.total_time = 0; set_timer_delta(&t, -2); for (int i = 0; i < N_LOOPS; i++) { add_timer(&t); do { ucs_twheel_sweep(&m_wheel, ucs_get_time()); } while (t.end_time == 0); } EXPECT_NEAR(t.total_time , t.d * N_LOOPS, 4 * N_LOOPS * m_wheel.res); #endif } UCS_TEST_F(twheel, delayed_sweep) { std::vector t(N_TIMERS); init_timerv(&t[0], N_TIMERS); for (int i = 0; i < N_TIMERS; i++) { set_timer_delta(&t[i], i); add_timer(&t[i]); } /* Sleep up to to seconds. Finish the test if the timers are triggered earlier, fail otherwise. */ for (int i = 0; i < 10; i++) { sleep(1); ucs_twheel_sweep(&m_wheel, ucs_get_time()); if (check_all_timers_triggered(t)) { return; } } /* all timers should have been triggered */ GTEST_FAIL() << "Timers were not triggered after timeout"; }