Changeset View
Changeset View
Standalone View
Standalone View
src/test/scheduler_tests.cpp
// Copyright (c) 2012-2019 The Bitcoin Core developers | // Copyright (c) 2012-2019 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <scheduler.h> | #include <scheduler.h> | ||||
#include <util/time.h> | #include <util/time.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <sync.h> | #include <sync.h> | ||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
#include <boost/thread.hpp> | |||||
#include <atomic> | #include <atomic> | ||||
#include <condition_variable> | #include <condition_variable> | ||||
#include <thread> | #include <thread> | ||||
BOOST_AUTO_TEST_SUITE(scheduler_tests) | BOOST_AUTO_TEST_SUITE(scheduler_tests) | ||||
static void microTask(CScheduler &s, boost::mutex &mutex, int &counter, | static void microTask(CScheduler &s, boost::mutex &mutex, int &counter, | ||||
int delta, | int delta, | ||||
boost::chrono::system_clock::time_point rescheduleTime) { | std::chrono::system_clock::time_point rescheduleTime) { | ||||
{ | { | ||||
boost::unique_lock<boost::mutex> lock(mutex); | boost::unique_lock<boost::mutex> lock(mutex); | ||||
counter += delta; | counter += delta; | ||||
} | } | ||||
boost::chrono::system_clock::time_point noTime = | std::chrono::system_clock::time_point noTime = | ||||
boost::chrono::system_clock::time_point::min(); | std::chrono::system_clock::time_point::min(); | ||||
if (rescheduleTime != noTime) { | if (rescheduleTime != noTime) { | ||||
CScheduler::Function f = | CScheduler::Function f = | ||||
std::bind(µTask, std::ref(s), std::ref(mutex), | std::bind(µTask, std::ref(s), std::ref(mutex), | ||||
std::ref(counter), -delta + 1, noTime); | std::ref(counter), -delta + 1, noTime); | ||||
s.schedule(f, rescheduleTime); | s.schedule(f, rescheduleTime); | ||||
} | } | ||||
} | } | ||||
Show All 21 Lines | BOOST_AUTO_TEST_CASE(manythreads) { | ||||
auto randomMsec = [](FastRandomContext &rc) -> int { | auto randomMsec = [](FastRandomContext &rc) -> int { | ||||
return -11 + int(rc.randrange(1012)); | return -11 + int(rc.randrange(1012)); | ||||
}; | }; | ||||
// [-1000, 1000] | // [-1000, 1000] | ||||
auto randomDelta = [](FastRandomContext &rc) -> int { | auto randomDelta = [](FastRandomContext &rc) -> int { | ||||
return -1000 + int(rc.randrange(2001)); | return -1000 + int(rc.randrange(2001)); | ||||
}; | }; | ||||
boost::chrono::system_clock::time_point start = | std::chrono::system_clock::time_point start = | ||||
boost::chrono::system_clock::now(); | std::chrono::system_clock::now(); | ||||
boost::chrono::system_clock::time_point now = start; | std::chrono::system_clock::time_point now = start; | ||||
boost::chrono::system_clock::time_point first, last; | std::chrono::system_clock::time_point first, last; | ||||
size_t nTasks = microTasks.getQueueInfo(first, last); | size_t nTasks = microTasks.getQueueInfo(first, last); | ||||
BOOST_CHECK(nTasks == 0); | BOOST_CHECK(nTasks == 0); | ||||
for (int i = 0; i < 100; ++i) { | for (int i = 0; i < 100; ++i) { | ||||
boost::chrono::system_clock::time_point t = | std::chrono::system_clock::time_point t = | ||||
now + boost::chrono::microseconds(randomMsec(rng)); | now + std::chrono::microseconds(randomMsec(rng)); | ||||
boost::chrono::system_clock::time_point tReschedule = | std::chrono::system_clock::time_point tReschedule = | ||||
now + boost::chrono::microseconds(500 + randomMsec(rng)); | now + std::chrono::microseconds(500 + randomMsec(rng)); | ||||
int whichCounter = zeroToNine(rng); | int whichCounter = zeroToNine(rng); | ||||
CScheduler::Function f = std::bind(µTask, std::ref(microTasks), | CScheduler::Function f = std::bind(µTask, std::ref(microTasks), | ||||
std::ref(counterMutex[whichCounter]), | std::ref(counterMutex[whichCounter]), | ||||
std::ref(counter[whichCounter]), | std::ref(counter[whichCounter]), | ||||
randomDelta(rng), tReschedule); | randomDelta(rng), tReschedule); | ||||
microTasks.schedule(f, t); | microTasks.schedule(f, t); | ||||
} | } | ||||
nTasks = microTasks.getQueueInfo(first, last); | nTasks = microTasks.getQueueInfo(first, last); | ||||
BOOST_CHECK(nTasks == 100); | BOOST_CHECK(nTasks == 100); | ||||
BOOST_CHECK(first < last); | BOOST_CHECK(first < last); | ||||
BOOST_CHECK(last > now); | BOOST_CHECK(last > now); | ||||
// As soon as these are created they will start running and servicing the | // As soon as these are created they will start running and servicing the | ||||
// queue | // queue | ||||
boost::thread_group microThreads; | boost::thread_group microThreads; | ||||
for (int i = 0; i < 5; i++) { | for (int i = 0; i < 5; i++) { | ||||
microThreads.create_thread( | microThreads.create_thread( | ||||
std::bind(&CScheduler::serviceQueue, µTasks)); | std::bind(&CScheduler::serviceQueue, µTasks)); | ||||
} | } | ||||
UninterruptibleSleep(std::chrono::microseconds{600}); | UninterruptibleSleep(std::chrono::microseconds{600}); | ||||
now = boost::chrono::system_clock::now(); | now = std::chrono::system_clock::now(); | ||||
// More threads and more tasks: | // More threads and more tasks: | ||||
for (int i = 0; i < 5; i++) { | for (int i = 0; i < 5; i++) { | ||||
microThreads.create_thread( | microThreads.create_thread( | ||||
std::bind(&CScheduler::serviceQueue, µTasks)); | std::bind(&CScheduler::serviceQueue, µTasks)); | ||||
} | } | ||||
for (int i = 0; i < 100; i++) { | for (int i = 0; i < 100; i++) { | ||||
boost::chrono::system_clock::time_point t = | std::chrono::system_clock::time_point t = | ||||
now + boost::chrono::microseconds(randomMsec(rng)); | now + std::chrono::microseconds(randomMsec(rng)); | ||||
boost::chrono::system_clock::time_point tReschedule = | std::chrono::system_clock::time_point tReschedule = | ||||
now + boost::chrono::microseconds(500 + randomMsec(rng)); | now + std::chrono::microseconds(500 + randomMsec(rng)); | ||||
int whichCounter = zeroToNine(rng); | int whichCounter = zeroToNine(rng); | ||||
CScheduler::Function f = std::bind(µTask, std::ref(microTasks), | CScheduler::Function f = std::bind(µTask, std::ref(microTasks), | ||||
std::ref(counterMutex[whichCounter]), | std::ref(counterMutex[whichCounter]), | ||||
std::ref(counter[whichCounter]), | std::ref(counter[whichCounter]), | ||||
randomDelta(rng), tReschedule); | randomDelta(rng), tReschedule); | ||||
microTasks.schedule(f, t); | microTasks.schedule(f, t); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(mockforward) { | ||||
// schedule jobs for 2, 5 & 8 minutes into the future | // schedule jobs for 2, 5 & 8 minutes into the future | ||||
int64_t min_in_milli = 60 * 1000; | int64_t min_in_milli = 60 * 1000; | ||||
scheduler.scheduleFromNow(dummy, 2 * min_in_milli); | scheduler.scheduleFromNow(dummy, 2 * min_in_milli); | ||||
scheduler.scheduleFromNow(dummy, 5 * min_in_milli); | scheduler.scheduleFromNow(dummy, 5 * min_in_milli); | ||||
scheduler.scheduleFromNow(dummy, 8 * min_in_milli); | scheduler.scheduleFromNow(dummy, 8 * min_in_milli); | ||||
// check taskQueue | // check taskQueue | ||||
boost::chrono::system_clock::time_point first, last; | std::chrono::system_clock::time_point first, last; | ||||
size_t num_tasks = scheduler.getQueueInfo(first, last); | size_t num_tasks = scheduler.getQueueInfo(first, last); | ||||
BOOST_CHECK_EQUAL(num_tasks, 3ul); | BOOST_CHECK_EQUAL(num_tasks, 3ul); | ||||
std::thread scheduler_thread([&]() { scheduler.serviceQueue(); }); | std::thread scheduler_thread([&]() { scheduler.serviceQueue(); }); | ||||
// bump the scheduler forward 5 minutes | // bump the scheduler forward 5 minutes | ||||
scheduler.MockForward(boost::chrono::seconds(5 * 60)); | scheduler.MockForward(std::chrono::seconds(5 * 60)); | ||||
// ensure scheduler has chance to process all tasks queued for before 1 ms | // ensure scheduler has chance to process all tasks queued for before 1 ms | ||||
// from now. | // from now. | ||||
scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, 1); | scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, 1); | ||||
scheduler_thread.join(); | scheduler_thread.join(); | ||||
// check that the queue only has one job remaining | // check that the queue only has one job remaining | ||||
num_tasks = scheduler.getQueueInfo(first, last); | num_tasks = scheduler.getQueueInfo(first, last); | ||||
BOOST_CHECK_EQUAL(num_tasks, 1ul); | BOOST_CHECK_EQUAL(num_tasks, 1ul); | ||||
// check that the dummy function actually ran | // check that the dummy function actually ran | ||||
BOOST_CHECK_EQUAL(counter, 2); | BOOST_CHECK_EQUAL(counter, 2); | ||||
// check that the time of the remaining job has been updated | // check that the time of the remaining job has been updated | ||||
boost::chrono::system_clock::time_point now = | std::chrono::system_clock::time_point now = | ||||
boost::chrono::system_clock::now(); | std::chrono::system_clock::now(); | ||||
int delta = | int delta = | ||||
boost::chrono::duration_cast<boost::chrono::seconds>(first - now) | std::chrono::duration_cast<std::chrono::seconds>(first - now).count(); | ||||
.count(); | |||||
// should be between 2 & 3 minutes from now | // should be between 2 & 3 minutes from now | ||||
BOOST_CHECK(delta > 2 * 60 && delta < 3 * 60); | BOOST_CHECK(delta > 2 * 60 && delta < 3 * 60); | ||||
} | } | ||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |