9#include <boost/test/unit_test.hpp>
16BOOST_AUTO_TEST_SUITE(scheduler_tests)
21 std::lock_guard<std::mutex> lock(mutex);
24 auto noTime = std::chrono::steady_clock::time_point::min();
25 if (rescheduleTime != noTime) {
27 s.schedule(f, rescheduleTime);
45 std::mutex counterMutex[10];
46 int counter[10] = { 0 };
49 auto randomMsec = [](
FastRandomContext& rc) ->
int {
return -11 + (int)rc.randrange(1012); };
50 auto randomDelta = [](
FastRandomContext& rc) ->
int {
return -1000 + (int)rc.randrange(2001); };
52 auto start = std::chrono::steady_clock::now();
54 std::chrono::steady_clock::time_point first, last;
58 for (
int i = 0; i < 100; ++i) {
59 auto t = now + std::chrono::microseconds(randomMsec(rng));
60 auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
61 int whichCounter = zeroToNine(rng);
63 std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
64 randomDelta(rng), tReschedule);
73 std::vector<std::thread> microThreads;
74 microThreads.reserve(10);
75 for (
int i = 0; i < 5; i++)
79 now = std::chrono::steady_clock::now();
82 for (
int i = 0; i < 5; i++)
84 for (
int i = 0; i < 100; i++) {
85 auto t = now + std::chrono::microseconds(randomMsec(rng));
86 auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
87 int whichCounter = zeroToNine(rng);
89 std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
90 randomDelta(rng), tReschedule);
97 for (
auto& thread: microThreads) {
98 if (thread.joinable()) thread.join();
102 for (
int i = 0; i < 10; i++) {
104 counterSum += counter[i];
111 std::condition_variable condvar;
115 const auto no_wait = [&](
const std::chrono::seconds& d) {
116 return condvar.wait_until(lock, std::chrono::steady_clock::now() - d);
119 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1}));
120 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::minutes{1}));
121 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1}));
122 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{10}));
123 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{100}));
124 BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1000}));
139 std::vector<std::thread> threads;
141 for (
int i = 0; i < 5; ++i) {
142 threads.emplace_back([&] { scheduler.
serviceQueue(); });
152 for (
int i = 0; i < 100; ++i) {
153 queue1.
insert([i, &counter1]() {
154 bool expectation = i == counter1++;
158 queue2.
insert([i, &counter2]() {
159 bool expectation = i == counter2++;
166 for (
auto& thread: threads) {
167 if (thread.joinable()) thread.join();
188 std::chrono::steady_clock::time_point first, last;
192 std::thread scheduler_thread([&]() { scheduler.
serviceQueue(); });
198 scheduler.
scheduleFromNow([&scheduler] { scheduler.
stop(); }, std::chrono::milliseconds{1});
199 scheduler_thread.join();
209 auto now = std::chrono::steady_clock::now();
210 int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
Simple class for background tasks that should be run periodically or once "after a while".
void MockForward(std::chrono::seconds delta_seconds) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Mock the scheduler to fast forward in time.
void serviceQueue() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Services the queue 'forever'.
size_t getQueueInfo(std::chrono::steady_clock::time_point &first, std::chrono::steady_clock::time_point &last) const EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Returns number of tasks waiting to be serviced, and first and last task times.
std::function< void()> Function
void StopWhenDrained() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Tell any threads running serviceQueue to stop when there is no work left to be done.
void stop() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Tell any threads running serviceQueue to stop as soon as the current task is done.
void scheduleFromNow(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Call f once after the delta has passed.
void schedule(Function f, std::chrono::steady_clock::time_point t) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Call func at/after time t.
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
Class used by CScheduler clients which may schedule multiple jobs which are required to be run serial...
void insert(std::function< void()> func) override EXCLUSIVE_LOCKS_REQUIRED(!m_callbacks_mutex)
Add a callback to be executed.
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
BOOST_AUTO_TEST_CASE(manythreads)
static void microTask(CScheduler &s, std::mutex &mutex, int &counter, int delta, std::chrono::steady_clock::time_point rescheduleTime)
#define WAIT_LOCK(cs, name)
void UninterruptibleSleep(const std::chrono::microseconds &n)