static void tick_timer (int now, void* data) { struct timer* timer = (struct timer*)data; const int old_interval = timer->interval; /* Compute and store the lateness, updating the total */ const int lateness = Sys_Milliseconds() - now; timer->total_lateness -= timer->recent_lateness[timer->next_lateness]; timer->recent_lateness[timer->next_lateness] = lateness; timer->total_lateness += lateness; timer->next_lateness++; timer->next_lateness %= TIMER_LATENESS_HISTORY; /* Is it time to check the mean yet? */ timer->next_check--; if (timer->next_check <= 0) { const int mean = timer->total_lateness / TIMER_LATENESS_HISTORY; /* We use a saturating counter to damp the adjustment */ /* When we stay above the high water mark, increase the interval */ if (mean > TIMER_LATENESS_HIGH) timer->checks_high = std::min(TIMER_CHECK_LAG, timer->checks_high + 1); else timer->checks_high = std::max(0, timer->checks_high - 1); if (timer->checks_high > TIMER_CHECK_LAG) timer->interval += 2; /* When we stay below the low water mark, decrease the interval */ if (mean < TIMER_LATENESS_LOW) timer->checks_low = std::min(TIMER_CHECK_LAG, timer->checks_high + 1); else timer->checks_low = std::max(0, timer->checks_low - 1); if (timer->checks_low > TIMER_CHECK_LAG) timer->interval -= 1; /* Note that we slow the timer more quickly than we speed it up, * so it should tend to settle down in the vicinity of the low * water mark */ timer->next_check = TIMER_CHECK_INTERVAL; } timer->interval = std::max(timer->interval, 1000 / timer->min_freq->integer); if (timer->interval != old_interval) Com_DPrintf(DEBUG_ENGINE, "Adjusted timer on %s to interval %d\n", timer->min_freq->name, timer->interval); try { timer->func(now, timer->data); } catch (comDrop_t const&) { } /* We correct for the lateness of this frame. We do not correct for * the time consumed by this frame - that's billed to the lateness * of future frames (so that the automagic slowdown can work) */ Schedule_Event(now + lateness + timer->interval, &tick_timer, nullptr, nullptr, timer); }
static void testScheduler (void) { scheduleEvent_t *one, *two, *three, *four, *five, *e; three = Schedule_Event(4, NULL, NULL, NULL, "three"); CU_ASSERT_EQUAL_FATAL(three->next, NULL); four = Schedule_Event(4, NULL, NULL, NULL, "four"); CU_ASSERT_EQUAL_FATAL(three->next, four); CU_ASSERT_EQUAL_FATAL(four->next, NULL); five = Schedule_Event(4, NULL, NULL, NULL, "five"); CU_ASSERT_EQUAL_FATAL(three->next, four); CU_ASSERT_EQUAL_FATAL(four->next, five); CU_ASSERT_EQUAL_FATAL(five->next, NULL); one = Schedule_Event(3, NULL, delayCheck, NULL, "one"); CU_ASSERT_EQUAL_FATAL(one->next, three); CU_ASSERT_EQUAL_FATAL(three->next, four); CU_ASSERT_EQUAL_FATAL(four->next, five); CU_ASSERT_EQUAL_FATAL(five->next, NULL); two = Schedule_Event(3, NULL, NULL, NULL, "two"); CU_ASSERT_EQUAL_FATAL(one->next, two); CU_ASSERT_EQUAL_FATAL(two->next, three); CU_ASSERT_EQUAL_FATAL(three->next, four); CU_ASSERT_EQUAL_FATAL(four->next, five); CU_ASSERT_EQUAL_FATAL(five->next, NULL); e = Dequeue_Event(1); CU_ASSERT_EQUAL(e, NULL); e = Dequeue_Event(2); CU_ASSERT_EQUAL(e, NULL); /* one is delayed via check function - so we get the 2nd event at the first dequeue here */ e = Dequeue_Event(3); CU_ASSERT_EQUAL_FATAL(e, two); /* now we are ready for the 1st event */ e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, one); /* the remaining events are in order */ e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, three); e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, four); e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, five); }
static void testBlocked (void) { std::string s_one("one"); std::string s_two("two"); std::string s_three("three"); std::string s_four("three"); const int delay = 10; event_func* f_oneFour = (event_func*)0xCAFED00D; event_func* f_twoThree = (event_func*)0xB16B00B5; ScheduleEventPtr one = Schedule_Event(3, f_oneFour, delayCheckBlocked, nullptr, static_cast<void*>(&s_one)); one->delayFollowing = delay; ScheduleEventPtr two = Schedule_Event(4 + delay, f_twoThree, nullptr, nullptr, static_cast<void*>(&s_two)); two->delayFollowing = delay; ScheduleEventPtr three = Schedule_Event(5 + delay, f_twoThree, nullptr, nullptr, static_cast<void*>(&s_three)); three->delayFollowing = delay; ScheduleEventPtr four = Schedule_Event(5, f_oneFour, delayCheckBlocked, nullptr, static_cast<void*>(&s_four)); four->delayFollowing = delay; ScheduleEventPtr e = Dequeue_Event(1); CU_ASSERT_FALSE(e); e = Dequeue_Event(2); CU_ASSERT_FALSE(e); e = Dequeue_Event(3); CU_ASSERT_FALSE(e); e = Dequeue_Event(5); CU_ASSERT_FALSE(e); delayCheckBlockedVal = true; e = Dequeue_Event(5); CU_ASSERT_FALSE(e); e = Dequeue_Event(5 + delay); CU_ASSERT_EQUAL_FATAL(e, one); e = Dequeue_Event(4 + delay); CU_ASSERT_EQUAL_FATAL(e, two); e = Dequeue_Event(4 + delay); CU_ASSERT_FALSE(e); e = Dequeue_Event(5 + delay); CU_ASSERT_EQUAL_FATAL(e, three); e = Dequeue_Event(5 + delay); CU_ASSERT_EQUAL_FATAL(e, four); }
static void testScheduler (void) { std::string s_one("one"); std::string s_two("two"); std::string s_three("three"); std::string s_four("four"); std::string s_five("five"); ScheduleEventPtr one = Schedule_Event(3, nullptr, nullptr, nullptr, static_cast<void*>(&s_one)); ScheduleEventPtr two = Schedule_Event(3, nullptr, nullptr, nullptr, static_cast<void*>(&s_two)); ScheduleEventPtr three = Schedule_Event(4, nullptr, nullptr, nullptr, static_cast<void*>(&s_three)); ScheduleEventPtr four = Schedule_Event(4, nullptr, nullptr, nullptr, static_cast<void*>(&s_four)); ScheduleEventPtr five = Schedule_Event(5, nullptr, nullptr, nullptr, static_cast<void*>(&s_five)); CU_ASSERT_EQUAL(Dequeue_Event(1000), one); CU_ASSERT_EQUAL(Dequeue_Event(1000), two); CU_ASSERT_EQUAL(Dequeue_Event(1000), three); CU_ASSERT_EQUAL(Dequeue_Event(1000), four); CU_ASSERT_EQUAL(Dequeue_Event(1000), five); CU_ASSERT_FALSE(Dequeue_Event(1000)); }
static void Schedule_Timer (cvar_t* freq, event_func* func, event_check_func* check, void* data) { struct timer* const timer = Mem_PoolAllocType(struct timer, com_genericPool); timer->min_freq = freq; timer->interval = 1000 / freq->integer; timer->next_lateness = 0; timer->total_lateness = 0; timer->next_check = TIMER_CHECK_INTERVAL; timer->checks_high = 0; timer->checks_low = 0; timer->func = func; timer->data = data; for (int i = 0; i < TIMER_LATENESS_HISTORY; i++) timer->recent_lateness[i] = 0; Schedule_Event(Sys_Milliseconds() + timer->interval, &tick_timer, check, nullptr, timer); }
static void testSchedulerCheck (void) { std::string s_one("one"); std::string s_two("two"); std::string s_three("three"); std::string s_four("four"); std::string s_five("five"); ScheduleEventPtr three = Schedule_Event(4, nullptr, nullptr, nullptr, static_cast<void*>(&s_three)); ScheduleEventPtr four = Schedule_Event(4, nullptr, nullptr, nullptr, static_cast<void*>(&s_four)); ScheduleEventPtr five = Schedule_Event(4, nullptr, nullptr, nullptr, static_cast<void*>(&s_five)); ScheduleEventPtr one = Schedule_Event(3, nullptr, delayCheck, nullptr, static_cast<void*>(&s_one)); ScheduleEventPtr two = Schedule_Event(3, nullptr, nullptr, nullptr, static_cast<void*>(&s_two)); ScheduleEventPtr e = Dequeue_Event(1); CU_ASSERT_FALSE(e); e = Dequeue_Event(2); CU_ASSERT_FALSE(e); /* one is delayed via check function - so we get the 2nd event at the first dequeue here */ e = Dequeue_Event(3); CU_ASSERT_EQUAL_FATAL(e, two); /* now we are ready for the 1st event */ e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, one); /* the remaining events are in order */ e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, three); e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, four); e = Dequeue_Event(5); CU_ASSERT_EQUAL_FATAL(e, five); }