text: // Copyright (c) 2015-2016 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 <boost/bind.hpp>
  #include <cassert>
  #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()) {
                  boost::chrono::system_clock::time_point timeToWaitFor =
                      taskQueue.begin()->first;
                  if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) ==
                      boost::cv_status::timeout) {
                      // Exit loop after timeout, it means we reached the time of
                      // the event
                      break;
                  }
              }
  #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;
      newTaskScheduled.notify_one();
  }
  
  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;
  }
  