Why does boost asio function expires_from_now() cancel a deadline_timer?

boost, boost-asio, c++, linux

Solution

When I try to get (not set!) the current expiry time using boost expires_from_now() is seems to actually cancel the timer, yet it actually runs as expected, but does finally not call the handler.

this assumption is incorrect. When you create your `deadline_timer`

m_pTimer = new boost::asio::deadline_timer(io_service1);
m_pTimer->async_wait(handler1);

you've told it to expire immediately. Then, when you start your `io_service`

m_pThread = new boost::thread(&Mytimer::runTimerThread, this);

you subsequently invoke

m_pTimer->expires_from_now(boost::posix_time::seconds(10));

which as the documentation describes clearly, this will cancel any outstanding handlers, emphasis added is mine

This function sets the expiry time. Any pending asynchronous wait operations will be cancelled. The handler for each cancelled operation will be invoked with the `boost::asio::error::operation_aborted` error code.

To resolve this, change your `Mytimer::startTimer()` method as follows

--- timer4.cc.orig  2012-04-13 11:18:47.000000000 -0500
+++ timer4.cc   2012-04-13 11:16:54.000000000 -0500
@@ -1,4 +1,3 @@
-
 #include <boost/asio.hpp> 
 #include <boost/thread.hpp> 
 #include <boost/date_time/posix_time/posix_time.hpp>
@@ -39,7 +38,6 @@
     m_pThread(NULL)
 {
     m_pTimer = new boost::asio::deadline_timer(io_service1); 
-    m_pTimer->async_wait(handler1); 
 }

 void Mytimer::runTimerThread()
@@ -49,8 +47,9 @@

 void Mytimer::startTimer()
 {
-    m_pThread = new boost::thread(&Mytimer::runTimerThread, this);
     m_pTimer->expires_from_now(boost::posix_time::seconds(10));
+    m_pTimer->async_wait(handler1); 
+    m_pThread = new boost::thread(&Mytimer::runTimerThread, this);
 }

 bool Mytimer::isRunning()

which results in the expected behavior

mbp:stackoverflow samm$ ./a.out
20120413 11:17:22922529 before timer construction
20120413 11:17:22923155 before startTimer()
20120413 11:17:22923530 IsRunning: 1
20120413 11:17:23924702 IsRunning: 1
20120413 11:17:24925971 IsRunning: 1
20120413 11:17:25927320 IsRunning: 1
20120413 11:17:26928715 IsRunning: 1
20120413 11:17:27929969 IsRunning: 1
20120413 11:17:28930601 IsRunning: 1
20120413 11:17:29931843 IsRunning: 1
20120413 11:17:30933098 IsRunning: 1
20120413 11:17:31934366 IsRunning: 0
20120413 11:17:32923594 Handler1: expired.
20120413 11:17:32934692 IsRunning: 0
20120413 11:17:33935922 IsRunning: 0
20120413 11:17:34936638 IsRunning: 0
20120413 11:17:35937905 IsRunning: 0
20120413 11:17:36939133 IsRunning: 0
20120413 11:17:37940407 IsRunning: 0
20120413 11:17:38941043 IsRunning: 0
20120413 11:17:39942319 IsRunning: 0
20120413 11:17:40942662 IsRunning: 0
20120413 11:17:41943989 IsRunning: 0
20120413 11:17:42945284 IsRunning: 0
20120413 11:17:43946555 IsRunning: 0
mbp:stackoverflow samm$ 

Problem

When I try to get (not set!) the current expiry time using boost expires_from_now() is seems to actually cancel the timer, yet it actually runs as expected, but does finally not call the handler. Or put in other words, when accessing a deadline_timer with expires_from_now() it will call the handler immediately and not call the handler when it expires. Please consider the following code and corresponding output: ``` #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time_io.hpp> #include <iostream> using namespace boost::posix_time; using namespace std; void handler1(const boost::system::error_code &ec) { if (ec == boost::asio::error::operation_aborted) { std::cout << microsec_clock::local_time() << " Handler1: Timer 1 was cancelled or retriggered." << std::endl; } else { std::cout << microsec_clock::local_time() << " Handler1: expired." << std::endl; } } boost::asio::io_service io_service1; class Mytimer { public: Mytimer(); void startTimer(); void runTimerThread(); bool isRunning(); private: bool m_isRunning; boost::asio::deadline_timer* m_pTimer; boost::thread* m_pThread; }; Mytimer::Mytimer() : m_pTimer(NULL), m_pThread(NULL) { m_pTimer = new boost::asio::deadline_timer(io_service1); m_pTimer->async_wait(handler1); } void Mytimer::runTimerThread() { io_service1.run(); } void Mytimer::startTimer() { m_pThread = new boost::thread(&Mytimer::runTimerThread, this); m_pTimer->expires_from_now(boost::posix_time::seconds(10)); } bool Mytimer::isRunning() { time_duration td = m_pTimer->expires_from_now(); if (td.total_seconds() > 0) { return true; } return false; } int main() { time_facet *facet = new time_facet("%Y%m%d %H:%M:%S%f"); std::cout.imbue(std::locale(std::cout.getloc(), facet)); cout << microsec_clock::local_time() << " before timer construction" << endl; Mytimer timer; cout << microsec_clock::local_time() << " before startTimer()" << endl; timer.startTimer(); cout << microsec_clock::local_time() << " IsRunning: " << timer.isRunning() << endl; for (int i = 0; i <= 20; i++) { sleep(1); cout << microsec_clock::local_time() << " IsRunning: " << timer.isRunning() << endl; } } ``` 20120412 22:41:45689235 before timer construction 20120412 22:41:45689514 before startTimer() 20120412 22:41:45689619 IsRunning: 1 20120412 22:41:45689693 Handler1: Timer 1 was cancelled or retriggered. 20120412 22:41:46689792 IsRunning: 1 20120412 22:41:47689994 IsRunning: 1 20120412 22:41:48690179 IsRunning: 1 20120412 22:41:49690375 IsRunning: 1 20120412 22:41:50690530 IsRunning: 1 20120412 22:41:51690712 IsRunning: 1 20120412 22:41:52690884 IsRunning: 1 20120412 22:41:53691069 IsRunning: 1 20120412 22:41:54691236 IsRunning: 0 20120412 22:41:55691428 IsRunning: 0 20120412 22:41:56691614 IsRunning: 0 20120412 22:41:57691810 IsRunning: 0 20120412 22:41:58692001 IsRunning: 0 20120412 22:41:59692193 IsRunning: 0 20120412 22:42:00692364 IsRunning: 0 20120412 22:42:01692542 IsRunning: 0 20120412 22:42:02692706 IsRunning: 0 20120412 22:42:03692886 IsRunning: 0 20120412 22:42:04693071 IsRunning: 0 20120412 22:42:05693267 IsRunning: 0 20120412 22:42:06693465 IsRunning: 0

Original source