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 <scheduler.h> | #include <scheduler.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <test/test_bitcoin.h> | #include <test/test_bitcoin.h> | ||||
#include <boost/bind.hpp> | |||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
#include <atomic> | #include <atomic> | ||||
#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) { | 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; | ||||
} | } | ||||
boost::chrono::system_clock::time_point noTime = | boost::chrono::system_clock::time_point noTime = | ||||
boost::chrono::system_clock::time_point::min(); | boost::chrono::system_clock::time_point::min(); | ||||
if (rescheduleTime != noTime) { | if (rescheduleTime != noTime) { | ||||
CScheduler::Function f = | CScheduler::Function f = | ||||
boost::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); | ||||
} | } | ||||
} | } | ||||
static void MicroSleep(uint64_t n) { | static void MicroSleep(uint64_t n) { | ||||
boost::this_thread::sleep_for(boost::chrono::microseconds(n)); | boost::this_thread::sleep_for(boost::chrono::microseconds(n)); | ||||
} | } | ||||
Show All 34 Lines | BOOST_AUTO_TEST_CASE(manythreads) { | ||||
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 = | 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 = std::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); | ||||
} | } | ||||
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)); | std::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)); | 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 = | 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 = std::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); | ||||
// ... wait until all the threads are done | // ... wait until all the threads are done | ||||
microThreads.join_all(); | microThreads.join_all(); | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) { | ||||
SingleThreadedSchedulerClient queue2(&scheduler); | SingleThreadedSchedulerClient queue2(&scheduler); | ||||
// create more threads than queues | // create more threads than queues | ||||
// if the queues only permit execution of one task at once then | // if the queues only permit execution of one task at once then | ||||
// the extra threads should effectively be doing nothing | // the extra threads should effectively be doing nothing | ||||
// if they don't we'll get out of order behaviour | // if they don't we'll get out of order behaviour | ||||
boost::thread_group threads; | boost::thread_group threads; | ||||
for (int i = 0; i < 5; ++i) { | for (int i = 0; i < 5; ++i) { | ||||
threads.create_thread( | threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); | ||||
boost::bind(&CScheduler::serviceQueue, &scheduler)); | |||||
} | } | ||||
// these are not atomic, if SinglethreadedSchedulerClient prevents | // these are not atomic, if SinglethreadedSchedulerClient prevents | ||||
// parallel execution at the queue level no synchronization should be | // parallel execution at the queue level no synchronization should be | ||||
// required here | // required here | ||||
int counter1 = 0; | int counter1 = 0; | ||||
int counter2 = 0; | int counter2 = 0; | ||||
Show All 24 Lines |