Asio Repeating Timer
From codegorilla
Contents |
The C++ Asio Repeating Timer
During my use of Asio from boost (before it was part of boost) my major issue was the timers. The timer system itself was very good but it was designed as a one hit wonder and rescheduling of another timer from within the completion of the first timer has many issues associated with it, not least the cancelling semantics for timers.
This lead me on to develop a repeating timer for Asio based on the existing timer infrastructure, and what I will be sharing with you here is my seconds attempt, the first whilst working reasonably well had an inherent race condition when attempting to cancel the timer which isn't nice when your looking for good clean and well behaved system that exits safely.
The Source
The timer is contained within a single header:
- basic_repeating_timer.h - defines the template basic_repeating_timer and the typedef repeating_timer.
The latest version is available via subversion, http://svn.codegorilla.co.uk/repeating_timer/trunk/include
The Timer
The template parameters of the basic_repeating_timer have the same meaning as for the boost asio basic_deadline_timer template, see boost docs for more information.
For the purpose of this interface description related to the typedef repeating_timer (a posix_time based timer).
- repeating_timer( io_service ) - constructor, take a reference to the service that will manage the asio.
- void start( repeat_interval, handler ) - initiates the timer with a repeating interval and will call the handler when the timer fires. Cancels any existing timer.
- void stop(), void cancel() - stops/cancels the timer (cancel just calls stop), will wait until the timer has completed if it has already fired in another thread.
- void change_interval( repeat_interval ) - allow you to adjust the repeat interval without cancelling the timer. The change will only take affect after the timer next fires, to make the change immediate use start.
Example
In this example I create 5 threads and 10 timers, each with intervals of 1 to 10 seconds. Once enter/return is hit on the keyboard then the timers are cancelled and the threads are joined.
#include <boost/asio.hpp> #include <boost/thread/thread.hpp> #include <boost/ptr_container/ptr_array.hpp> #include "basic_repeating_timer.h" // callback hander for out timers void handle_timeout( int id, boost::system::error_code const& error ) { std::cout << "timer " << id << " fired\n"; } void example() { // example shows io_service running in multiple thread, with multiple timers boost::thread_group threads; boost::asio::io_service my_io_service; boost::ptr_array<boost::asio::repeating_timer,10> timers; // create timers for( size_t i = 0; i < timers.size(); ++i ) { timers.replace( i, new boost::asio::repeating_timer(my_io_service) ); } // start them with their callbacks referencing the next timer for( size_t i = 0; i < timers.size(); ++i ) { timers[i].start( boost::posix_time::seconds(i+1), boost::bind( &handle_timeout, i, boost::asio::placeholders::error ) ); } std::cout << "Press enter to stop the timers...\n"; // finally we start the service running. If the service has nothing to work // with it will exit immediately. for( size_t i = 0; i < 5; ++i ) { threads.create_thread( boost::bind(&boost::asio::io_service::run, &my_io_service) ); } // wait for the enter/return key to be pressed std::cin.get(); // explicitly cancel the timers // could just call timers.release() which would destroy the timer objects but // for completeness I wanted to show the cancel. for( size_t i = 0; i < timers.size(); ++i ) { timers[i].cancel(); } // and then wait for all threads to complete threads.join_all(); }
The Facts
- It is safe to start/stop/cancel or adjust the timer during the timer callback handler.
- If the callback is a member function, then the timer must be cancelled or destroyed (goes out of scope, whatever) before the class is destroyed (the classes destructor is safe to cancel the timer).
- If an (std::)exception is thrown in the callback handler and not caught then the timer catches it and continues execution (and will fire again after the interval).
- A repeating_timer will repeat forever unless it is cancelled or destroyed.
