Changeset View
Changeset View
Standalone View
Standalone View
src/test/scheduler_tests.cpp
// Copyright (c) 2012-2016 The Bitcoin Core developers | // Copyright (c) 2012-2016 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 "random.h" | #include "random.h" | ||||
#include "scheduler.h" | #include "scheduler.h" | ||||
#include "test/test_bitcoin.h" | #include "test/test_bitcoin.h" | ||||
#include <boost/bind.hpp> | #include <boost/bind.hpp> | ||||
#include <boost/random/mersenne_twister.hpp> | #include <boost/random/mersenne_twister.hpp> | ||||
#include <boost/random/uniform_int_distribution.hpp> | #include <boost/random/uniform_int_distribution.hpp> | ||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
#include <boost/thread.hpp> | #include <boost/thread.hpp> | ||||
#include <atomic> | |||||
#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) { | boost::chrono::system_clock::time_point rescheduleTime) { | ||||
{ | { | ||||
boost::unique_lock<boost::mutex> lock(mutex); | boost::unique_lock<boost::mutex> lock(mutex); | ||||
counter += delta; | counter += delta; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(manythreads) { | ||||
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( | ||||
boost::bind(&CScheduler::serviceQueue, µTasks)); | boost::bind(&CScheduler::serviceQueue, µTasks)); | ||||
} | |||||
MicroSleep(600); | MicroSleep(600); | ||||
now = boost::chrono::system_clock::now(); | now = boost::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( | ||||
boost::bind(&CScheduler::serviceQueue, µTasks)); | boost::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 = | boost::chrono::system_clock::time_point t = | ||||
now + boost::chrono::microseconds(randomMsec(rng)); | now + boost::chrono::microseconds(randomMsec(rng)); | ||||
boost::chrono::system_clock::time_point tReschedule = | boost::chrono::system_clock::time_point tReschedule = | ||||
now + boost::chrono::microseconds(500 + randomMsec(rng)); | now + boost::chrono::microseconds(500 + randomMsec(rng)); | ||||
int whichCounter = zeroToNine(rng); | int whichCounter = zeroToNine(rng); | ||||
CScheduler::Function f = boost::bind( | CScheduler::Function f = boost::bind( | ||||
µTask, std::ref(microTasks), | µTask, std::ref(microTasks), | ||||
std::ref(counterMutex[whichCounter]), | std::ref(counterMutex[whichCounter]), | ||||
std::ref(counter[whichCounter]), randomDelta(rng), tReschedule); | std::ref(counter[whichCounter]), randomDelta(rng), tReschedule); | ||||
microTasks.schedule(f, t); | microTasks.schedule(f, t); | ||||
} | } | ||||
// Drain the task queue then exit threads | // Drain the task queue then exit threads | ||||
microTasks.stop(true); | microTasks.stop(true); | ||||
microThreads.join_all(); // ... wait until all the threads are done | // ... wait until all the threads are done | ||||
microThreads.join_all(); | |||||
int counterSum = 0; | int counterSum = 0; | ||||
for (int i = 0; i < 10; i++) { | for (int i = 0; i < 10; i++) { | ||||
BOOST_CHECK(counter[i] != 0); | BOOST_CHECK(counter[i] != 0); | ||||
counterSum += counter[i]; | counterSum += counter[i]; | ||||
} | } | ||||
BOOST_CHECK_EQUAL(counterSum, 200); | BOOST_CHECK_EQUAL(counterSum, 200); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(schedule_every) { | |||||
CScheduler scheduler; | |||||
boost::condition_variable cvar; | |||||
std::atomic<int> counter{15}; | |||||
std::atomic<bool> keepRunning{true}; | |||||
scheduler.scheduleEvery( | |||||
[&keepRunning, &cvar, &counter, &scheduler]() { | |||||
BOOST_CHECK(counter > 0); | |||||
cvar.notify_all(); | |||||
if (--counter > 0) { | |||||
return true; | |||||
} | |||||
// We reached the end of our test, make sure nothing run again for | |||||
// 100ms. | |||||
scheduler.scheduleFromNow( | |||||
[&keepRunning, &cvar]() { | |||||
keepRunning = false; | |||||
cvar.notify_all(); | |||||
}, | |||||
100); | |||||
// We set the counter to some magic value to check the scheduler | |||||
// empty its queue properly after 120ms. | |||||
scheduler.scheduleFromNow([&counter]() { counter = 42; }, 120); | |||||
return false; | |||||
}, | |||||
5); | |||||
// Start the scheduler thread. | |||||
std::thread schedulerThread( | |||||
std::bind(&CScheduler::serviceQueue, &scheduler)); | |||||
boost::mutex mutex; | |||||
boost::unique_lock<boost::mutex> lock(mutex); | |||||
while (keepRunning) { | |||||
cvar.wait(lock); | |||||
BOOST_CHECK(counter >= 0); | |||||
} | |||||
BOOST_CHECK_EQUAL(counter, 0); | |||||
scheduler.stop(true); | |||||
schedulerThread.join(); | |||||
BOOST_CHECK_EQUAL(counter, 42); | |||||
} | |||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |