diff options
Diffstat (limited to 'src/scheduler.cpp')
-rw-r--r-- | src/scheduler.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/scheduler.cpp b/src/scheduler.cpp new file mode 100644 index 0000000000..184ddc28ab --- /dev/null +++ b/src/scheduler.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "scheduler.h" + +#include "reverselock.h" + +#include <assert.h> +#include <boost/bind.hpp> +#include <utility> + +CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) +{ +} + +CScheduler::~CScheduler() +{ + assert(nThreadsServicingQueue == 0); +} + + +#if BOOST_VERSION < 105000 +static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) +{ + return boost::posix_time::from_time_t(boost::chrono::system_clock::to_time_t(t)); +} +#endif + +void CScheduler::serviceQueue() +{ + boost::unique_lock<boost::mutex> lock(newTaskMutex); + ++nThreadsServicingQueue; + + // newTaskMutex is locked throughout this loop EXCEPT + // when the thread is waiting or when the user's function + // is called. + while (!shouldStop()) { + try { + while (!shouldStop() && taskQueue.empty()) { + // Wait until there is something to do. + newTaskScheduled.wait(lock); + } + + // Wait until either there is a new task, or until + // the time of the first item on the queue: + +// wait_until needs boost 1.50 or later; older versions have timed_wait: +#if BOOST_VERSION < 105000 + while (!shouldStop() && !taskQueue.empty() && + newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { + // Keep waiting until timeout + } +#else + // Some boost versions have a conflicting overload of wait_until that returns void. + // Explicitly use a template here to avoid hitting that overload. + while (!shouldStop() && !taskQueue.empty() && + newTaskScheduled.wait_until<>(lock, taskQueue.begin()->first) != boost::cv_status::timeout) { + // Keep waiting until timeout + } +#endif + // If there are multiple threads, the queue can empty while we're waiting (another + // thread may service the task we were waiting on). + if (shouldStop() || taskQueue.empty()) + continue; + + Function f = taskQueue.begin()->second; + taskQueue.erase(taskQueue.begin()); + + { + // Unlock before calling f, so it can reschedule itself or another task + // without deadlocking: + reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); + f(); + } + } catch (...) { + --nThreadsServicingQueue; + throw; + } + } + --nThreadsServicingQueue; +} + +void CScheduler::stop(bool drain) +{ + { + boost::unique_lock<boost::mutex> lock(newTaskMutex); + if (drain) + stopWhenEmpty = true; + else + stopRequested = true; + } + newTaskScheduled.notify_all(); +} + +void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) +{ + { + boost::unique_lock<boost::mutex> lock(newTaskMutex); + taskQueue.insert(std::make_pair(t, f)); + } + newTaskScheduled.notify_one(); +} + +void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaSeconds) +{ + schedule(f, boost::chrono::system_clock::now() + boost::chrono::seconds(deltaSeconds)); +} + +static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaSeconds) +{ + f(); + s->scheduleFromNow(boost::bind(&Repeat, s, f, deltaSeconds), deltaSeconds); +} + +void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaSeconds) +{ + scheduleFromNow(boost::bind(&Repeat, this, f, deltaSeconds), deltaSeconds); +} + +size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, + boost::chrono::system_clock::time_point &last) const +{ + boost::unique_lock<boost::mutex> lock(newTaskMutex); + size_t result = taskQueue.size(); + if (!taskQueue.empty()) { + first = taskQueue.begin()->first; + last = taskQueue.rbegin()->first; + } + return result; +} |