Skip to content

Commit

Permalink
High resolution timer scheduler
Browse files Browse the repository at this point in the history
Based on userland active busy loop instead of passive waiting event
#3
  • Loading branch information
securesocketfunneling committed Sep 23, 2015
1 parent 1e4037e commit 41fcb8a
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/timer_benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ set(project_NAME "timer_benchmark")
project(${project_NAME})

set(TIMER_FILES
high_precision_timer_scheduler.hpp
detail/high_precision_timer_scheduler.ipp
main.cpp)

include_directories(
Expand Down
176 changes: 176 additions & 0 deletions src/timer_benchmark/detail/high_precision_timer_scheduler.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//
// detail/high_precision_timer_scheduler.ipp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

#ifndef DETAIL_HIGH_PRECISION_TIMER_IPP
#define DETAIL_HIGH_PRECISION_TIMER_IPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <chrono>

#include <boost/asio/detail/bind_handler.hpp>
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/timer_scheduler.hpp>

#include <boost/chrono.hpp>

#include <timer_benchmark/high_precision_timer_scheduler.hpp>

#include <boost/asio/detail/push_options.hpp>

high_precision_timer_scheduler::high_precision_timer_scheduler(boost::asio::io_service& io_service)
: boost::asio::detail::service_base<high_precision_timer_scheduler>(io_service),
io_service_(boost::asio::use_service<boost::asio::detail::io_service_impl>(io_service)),
mutex_(),
timer_queues_(),
thread_(0),
stop_thread_(false),
shutdown_(false)
{
thread_ = new boost::asio::detail::thread(
boost::asio::detail::bind_handler(&high_precision_timer_scheduler::call_run_thread, this));
}


high_precision_timer_scheduler::~high_precision_timer_scheduler() {
shutdown_service();
}

void high_precision_timer_scheduler::shutdown_service() {
using boost::asio::detail::operation;
std::unique_lock<std::mutex> lock(mutex_);

shutdown_ = true;
stop_thread_ = true;
cv_.notify_all();
lock.unlock();

if (thread_) {
thread_->join();
delete thread_;
thread_ = 0;
}

boost::asio::detail::op_queue<operation> ops;
timer_queues_.get_all_timers(ops);
io_service_.abandon_operations(ops);
}

void high_precision_timer_scheduler::fork_service(boost::asio::io_service::fork_event fork_ev) {

}

void high_precision_timer_scheduler::init_task() {

}

template <typename Time_Traits>
void high_precision_timer_scheduler::add_timer_queue(timer_queue<Time_Traits>& queue) {
do_add_timer_queue(queue);
}

template <typename Time_Traits>
void high_precision_timer_scheduler::remove_timer_queue(timer_queue<Time_Traits>& queue) {
do_remove_timer_queue(queue);
}

template <typename Time_Traits>
void high_precision_timer_scheduler::schedule_timer(timer_queue<Time_Traits>& queue,
const typename Time_Traits::time_type& time,
typename timer_queue<Time_Traits>::per_timer_data& timer,
boost::asio::detail::wait_op* op) {
boost::asio::detail::scoped_lock<std::mutex> lock(mutex_);

if (shutdown_) {
io_service_.post_immediate_completion(op, false);
return;
}

bool earliest = queue.enqueue_timer(time, timer, op);
io_service_.work_started();

if (earliest) {
cv_.notify_all();
}
}

template <typename Time_Traits>
std::size_t high_precision_timer_scheduler::cancel_timer(timer_queue<Time_Traits>& queue,
typename timer_queue<Time_Traits>::per_timer_data& timer,
std::size_t max_cancelled) {
boost::asio::detail::scoped_lock<std::mutex> lock(mutex_);
boost::asio::detail::op_queue<boost::asio::detail::operation> ops;
std::size_t n = queue.cancel_timer(timer, ops, max_cancelled);
lock.unlock();
io_service_.post_deferred_completions(ops);
return n;
}

void high_precision_timer_scheduler::run_thread() {
using Clock = std::chrono::high_resolution_clock;
using boost::asio::detail::operation;

std::unique_lock<std::mutex> lock(mutex_);

while (!stop_thread_) {
const long max_wait_duration = 5 * 60 * 1000000;
long wait_duration = timer_queues_.wait_duration_usec(max_wait_duration);

if (wait_duration == max_wait_duration) {
auto timeout = Clock::now() + std::chrono::microseconds(wait_duration);
cv_.wait_until(lock, timeout);
continue;
}

active_wait_usec(wait_duration);

boost::asio::detail::op_queue<operation> ops;
timer_queues_.get_ready_timers(ops);
if (!ops.empty()) {
lock.unlock();
io_service_.post_deferred_completions(ops);
lock.lock();
}
}
}


void high_precision_timer_scheduler::call_run_thread(high_precision_timer_scheduler* scheduler) {
scheduler->run_thread();
}

void high_precision_timer_scheduler::do_add_timer_queue(timer_queue_base& queue) {
std::unique_lock<std::mutex> lock(mutex_);
timer_queues_.insert(&queue);
}


void high_precision_timer_scheduler::do_remove_timer_queue(timer_queue_base& queue) {
std::unique_lock<std::mutex> lock(mutex_);
timer_queues_.erase(&queue);
}


void high_precision_timer_scheduler::active_wait_usec(long wait_duration) {
using Clock = boost::chrono::high_resolution_clock;

auto timeout = Clock::now() + boost::chrono::microseconds(wait_duration);

while (Clock::now() < timeout) {
#ifdef BOOST_ASIO_WINDOWS
std::this_thread::sleep_for(std::chrono::seconds(0));
#else
// Bad performance on linux with std::this_thread::sleep_for.
sleep(0);
#endif
}

}

#include <boost/asio/detail/pop_options.hpp>

#endif // DETAIL_HIGH_PRECISION_TIMER_IPP
128 changes: 128 additions & 0 deletions src/timer_benchmark/high_precision_timer_scheduler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//
// high_precision_timer_scheduler.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//


#ifndef HIGH_PRECISION_TIMER_SCHEDULER_HPP
#define HIGH_PRECISION_TIMER_SCHEDULER_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <boost/asio/detail/config.hpp>

#include <cstddef>
#include <mutex>
#include <condition_variable>

#include <boost/asio/detail/event.hpp>
#include <boost/asio/detail/limits.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/asio/detail/op_queue.hpp>
#include <boost/asio/detail/thread.hpp>
#include <boost/asio/detail/timer_queue_base.hpp>
#include <boost/asio/detail/timer_queue_set.hpp>
#include <boost/asio/detail/wait_op.hpp>
#include <boost/asio/io_service.hpp>

#if defined(BOOST_ASIO_HAS_IOCP)
# include <boost/asio/detail/thread.hpp>
#endif // defined(BOOST_ASIO_HAS_IOCP)

#include <boost/asio/detail/push_options.hpp>


class high_precision_timer_scheduler
: public boost::asio::detail::service_base<high_precision_timer_scheduler> {

public:
template <typename Time_trait>
using timer_queue = boost::asio::detail::timer_queue<Time_trait>;

using timer_queue_base = boost::asio::detail::timer_queue_base;

using timer_queue_set = boost::asio::detail::timer_queue_set;

// Constructor
BOOST_ASIO_DECL high_precision_timer_scheduler(boost::asio::io_service& io_service);

// Destructor
BOOST_ASIO_DECL ~high_precision_timer_scheduler();

// Destroy all user-defined handler objects owned by the service.
BOOST_ASIO_DECL void shutdown_service();

// Recreate internal descriptors following a fork.
BOOST_ASIO_DECL void fork_service(boost::asio::io_service::fork_event fork_ev);

// Initialise the task. No effect as this class uses its own thread.
BOOST_ASIO_DECL void init_task();

// Add a new timer queue to the reactor.
template <typename Time_Traits>
void add_timer_queue(timer_queue<Time_Traits>& queue);

// Remove a timer queue from the reactor.
template <typename Time_Traits>
void remove_timer_queue(timer_queue<Time_Traits>& queue);

// Schedule a new operation in the given timer queue to expire at the
// specified absolute time.
template <typename Time_Traits>
void schedule_timer(timer_queue<Time_Traits>& queue,
const typename Time_Traits::time_type& time,
typename timer_queue<Time_Traits>::per_timer_data& timer, boost::asio::detail::wait_op* op);

// Cancel the timer operations associated with the given token. Returns the
// number of operations that have been posted or dispatched.
template <typename Time_Traits>
std::size_t cancel_timer(timer_queue<Time_Traits>& queue,
typename timer_queue<Time_Traits>::per_timer_data& timer,
std::size_t max_cancelled = (std::numeric_limits<std::size_t>::max)());


private:
// Run the select loop in the thread.
BOOST_ASIO_DECL void run_thread();

// Entry point for the select loop thread.
BOOST_ASIO_DECL static void call_run_thread(high_precision_timer_scheduler* reactor);

// Helper function to add a new timer queue.
BOOST_ASIO_DECL void do_add_timer_queue(timer_queue_base& queue);

// Helper function to remove a timer queue.
BOOST_ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue);

void active_wait_usec(long wait_duration);

// The io_service implementation used to post completions.
boost::asio::detail::io_service_impl& io_service_;

// Mutex used to protect internal variables.
std::mutex mutex_;

// Condition variable used to notify_all to wake up background thread.
std::condition_variable cv_;

// The timer queues.
timer_queue_set timer_queues_;

// The background thread that is waiting for timers to expire.
boost::asio::detail::thread* thread_;

// Does the background thread need to stop.
bool stop_thread_;

// Whether the service has been shut down.
bool shutdown_;
};


#include <boost/asio/detail/pop_options.hpp>

#include <timer_benchmark/detail/high_precision_timer_scheduler.ipp>

#endif // HIGH_PRECISION_TIMER_SCHEDULER_HPP
9 changes: 8 additions & 1 deletion src/timer_benchmark/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define BOOST_ASIO_HAS_STD_MUTEX_AND_CONDVAR

#include <future>
#include <iostream>

Expand All @@ -8,6 +10,8 @@
#include <boost/asio/basic_waitable_timer.hpp>

#include <boost/thread.hpp>
#include <timer_benchmark/high_precision_timer_scheduler.hpp>


using Accumulator = boost::accumulators::accumulator_set<
int, boost::accumulators::features<
Expand All @@ -19,10 +23,13 @@ void DisplayStatistics(const Accumulator& statistics, int expected_wait);
int main(int argc, char* argv[]) {
namespace chrono = boost::chrono;
using Clock = chrono::high_resolution_clock;
using ClockTimer = boost::asio::basic_waitable_timer<Clock>;
using ClockTimer = boost::asio::basic_waitable_timer<
Clock, boost::asio::wait_traits<Clock>, high_precision_timer_scheduler>;

using ClockTimePoint = chrono::time_point<Clock>;
using TimerHandler = std::function<void(const boost::system::error_code&)>;


if (argc < 3) {
std::cout << "timer_benchmark [wait_usec] [sample_size]" << std::endl;
return 0;
Expand Down

0 comments on commit 41fcb8a

Please sign in to comment.