diff --git a/asio/include/asio/basic_timer.hpp b/asio/include/asio/basic_timer.hpp index 24e6b185..caa09024 100644 --- a/asio/include/asio/basic_timer.hpp +++ b/asio/include/asio/basic_timer.hpp @@ -145,21 +145,25 @@ public: service_.set(impl_, from_when, seconds, microseconds); } - /// Expire the timer immediately. + /// Cancel any asynchronous operations that are waiting on the timer. /** - * This function causes the timer to expire immediately. If there is a - * pending asynchronous wait operation against the timer it will be forced to - * complete. + * This function forces the completion of any pending asynchronous wait + * operations against the timer. The handler for each cancelled operation + * will be invoked with the asio::error::operation_aborted error code. + * + * @return The number of asynchronous operations that were cancelled. */ - void expire() + int cancel() { - service_.expire(impl_); + return service_.cancel(impl_); } /// Perform a blocking wait on the timer. /** * This function is used to wait for the timer to expire. This function * blocks and does not return until the timer has expired. + * + * @throws asio::error Thrown on failure. */ void wait() { @@ -170,11 +174,14 @@ public: /** * This function may be used to initiate an asynchronous wait against the * timer. It always returns immediately, but the specified handler will be - * notified when the timer expires. + * notified when the timer expires, or if the operation is cancelled. * * @param handler The handler to be called when the timer expires. Copies * will be made of the handler as required. The equivalent function signature - * of the handler must be: @code void handler(); @endcode + * of the handler must be: + * @code void handler( + * const asio::error& error // Result of operation + * ); @endcode */ template void async_wait(Handler handler) diff --git a/asio/include/asio/detail/reactive_timer_service.hpp b/asio/include/asio/detail/reactive_timer_service.hpp index e4a013ae..9fed7a62 100644 --- a/asio/include/asio/detail/reactive_timer_service.hpp +++ b/asio/include/asio/detail/reactive_timer_service.hpp @@ -21,8 +21,10 @@ #include #include "asio/detail/pop_options.hpp" +#include "asio/error.hpp" #include "asio/service_factory.hpp" #include "asio/timer_base.hpp" +#include "asio/detail/bind_handler.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/time.hpp" @@ -78,7 +80,7 @@ public: { if (impl != null()) { - reactor_.expire_timer(impl); + reactor_.cancel_timer(impl); delete impl; impl = null(); } @@ -106,11 +108,10 @@ public: } } - // Expire the timer immediately. - void expire(impl_type& impl) + // Cancel any asynchronous wait operations associated with the timer. + int cancel(impl_type& impl) { - impl->expiry = time::now(); - reactor_.expire_timer(impl); + return reactor_.cancel_timer(impl); } // Perform a blocking wait on the timer. @@ -142,13 +143,15 @@ public: void do_operation() { - demuxer_.post(handler_); + asio::error e(asio::error::success); + demuxer_.post(detail::bind_handler(handler_, e)); demuxer_.work_finished(); } void do_cancel() { - demuxer_.post(handler_); + asio::error e(asio::error::operation_aborted); + demuxer_.post(detail::bind_handler(handler_, e)); demuxer_.work_finished(); } diff --git a/asio/include/asio/detail/reactor_timer_queue.hpp b/asio/include/asio/detail/reactor_timer_queue.hpp index 748eced6..55b03bd6 100644 --- a/asio/include/asio/detail/reactor_timer_queue.hpp +++ b/asio/include/asio/detail/reactor_timer_queue.hpp @@ -94,8 +94,9 @@ public: // Cancel the timer with the given token. The handler's do_cancel operation // will be invoked immediately. - void cancel_timer(void* timer_token) + int cancel_timer(void* timer_token) { + int num_cancelled = 0; typedef typename hash_map::iterator iterator; iterator it = timers_.find(timer_token); if (it != timers_.end()) @@ -107,8 +108,10 @@ public: remove_timer(t); t->do_cancel(); t = next; + ++num_cancelled; } } + return num_cancelled; } private: diff --git a/asio/include/asio/detail/select_reactor.hpp b/asio/include/asio/detail/select_reactor.hpp index 48a822a9..457dd44a 100644 --- a/asio/include/asio/detail/select_reactor.hpp +++ b/asio/include/asio/detail/select_reactor.hpp @@ -168,11 +168,12 @@ public: interrupter_.interrupt(); } - // Cancel the timer associated with the given token. - void expire_timer(void* token) + // Cancel the timer associated with the given token. Returns the number of + // handlers that have been posted or dispatched. + int cancel_timer(void* token) { asio::detail::mutex::scoped_lock lock(mutex_); - timer_queue_.cancel_timer(token); + return timer_queue_.cancel_timer(token); } private: diff --git a/asio/src/examples/tutorial/timer2/timer.cpp b/asio/src/examples/tutorial/timer2/timer.cpp index 54b60e4c..62d4d5ae 100644 --- a/asio/src/examples/tutorial/timer2/timer.cpp +++ b/asio/src/examples/tutorial/timer2/timer.cpp @@ -1,7 +1,7 @@ #include #include "asio.hpp" -void print() +void print(const asio::error& /*e*/) { std::cout << "Hello, world!\n"; } diff --git a/asio/src/examples/tutorial/timer3/timer.cpp b/asio/src/examples/tutorial/timer3/timer.cpp index c8d68411..2aa0b61c 100644 --- a/asio/src/examples/tutorial/timer3/timer.cpp +++ b/asio/src/examples/tutorial/timer3/timer.cpp @@ -2,7 +2,7 @@ #include "boost/bind.hpp" #include "asio.hpp" -void print(asio::timer* t, int* count) +void print(const asio::error& /*e*/, asio::timer* t, int* count) { if (*count < 5) { @@ -10,7 +10,7 @@ void print(asio::timer* t, int* count) ++(*count); t->set(asio::timer::from_existing, 1); - t->async_wait(boost::bind(print, t, count)); + t->async_wait(boost::bind(print, asio::arg::error, t, count)); } } @@ -20,7 +20,7 @@ int main() int count = 0; asio::timer t(d, asio::timer::from_now, 1); - t.async_wait(boost::bind(print, &t, &count)); + t.async_wait(boost::bind(print, asio::arg::error, &t, &count)); d.run(); diff --git a/asio/src/examples/tutorial/timer_dox.txt b/asio/src/examples/tutorial/timer_dox.txt index f37606dd..f1237fd2 100644 --- a/asio/src/examples/tutorial/timer_dox.txt +++ b/asio/src/examples/tutorial/timer_dox.txt @@ -158,13 +158,21 @@ from the whole-second mark due to any delays in processing the handler. Step 4. Then we start a new asynchronous wait on the timer. As you can see, the \ref boost_bind function is used to associate the extra parameters with your callback handler. The asio::timer::async_wait() function expects a -handler function (or function object) with the signature void(). -Binding the additional parameters converts your print function into a -function object that matches the signature correctly. +handler function (or function object) with the signature void(const +asio::error&). Binding the additional parameters converts your +print function into a function object that matches the signature +correctly. See the Boost: bind.hpp documentation for more information on how to use \ref boost_bind. +In this example, the asio::arg:error argument to \ref boost_bind is a named +placeholder for the error object passed to the handler. When initiating the +asynchronous operation, and if using \ref boost_bind, you must specify only +the arguments that match the handler's parameter list. In tutorial Timer.4 you +will see that this placeholder may be elided if the parameter is not needed by +the callback handler. + \until asio::demuxer Step 5. A new count variable is added so that we can stop the @@ -225,7 +233,12 @@ member functions as with free functions. Since all non-static class member functions have an implicit this parameter, we need to bind this to the function. As in tutorial Timer.3, \ref boost_bind converts our callback handler (now a member function) into a function object -that matches the signature void(). +that can be invoked as though it has the signature void(const +asio::error&). + +You will note that the asio::arg::error placeholder is not specified here, as +the print member function does not accept an error object as a +parameter. \until } diff --git a/asio/src/tests/unit/timer_test.cpp b/asio/src/tests/unit/timer_test.cpp index 8ac86e5d..4a41cc99 100644 --- a/asio/src/tests/unit/timer_test.cpp +++ b/asio/src/tests/unit/timer_test.cpp @@ -39,16 +39,16 @@ void decrement_to_zero(timer* t, int* count) } } -void increment_if_not_expired(int* count, bool* expired) +void increment_if_not_cancelled(int* count, const error& e) { - if (!*expired) + if (!e) ++(*count); } -void expire_timer(timer* t, bool* expired) +void cancel_timer(timer* t) { - *expired = true; - t->expire(); + int num_cancelled = t->cancel(); + UNIT_TEST_CHECK(num_cancelled == 1); } void timer_test() @@ -134,12 +134,11 @@ void timer_test() count = 0; start = detail::time::now(); - bool expired = false; - timer t5(d, timer::from_now, 60); - t5.async_wait(boost::bind(increment_if_not_expired, &count, &expired)); + timer t5(d, timer::from_now, 10); + t5.async_wait(boost::bind(increment_if_not_cancelled, &count, arg::error)); timer t6(d, timer::from_now, 1); - t6.async_wait(boost::bind(expire_timer, &t5, &expired)); + t6.async_wait(boost::bind(cancel_timer, &t5)); // No completions can be delivered until run() is called. UNIT_TEST_CHECK(count == 0); @@ -147,14 +146,29 @@ void timer_test() d.reset(); d.run(); - // The timer should have been expired, so count should not have changed. + // The timer should have been cancelled, so count should not have changed. // The total run time should not have been much more than 1 second (and - // certainly far less than 60 seconds). + // certainly far less than 10 seconds). UNIT_TEST_CHECK(count == 0); end = detail::time::now(); expected_end = start; expected_end += detail::time(2); UNIT_TEST_CHECK(end < expected_end); + + // Wait on the timer again without cancelling it. This time the asynchronous + // wait should run to completion and increment the counter. + t5.async_wait(boost::bind(increment_if_not_cancelled, &count, arg::error)); + + d.reset(); + d.run(); + + // The timer should not have been cancelled, so count should have changed. + // The total time since the timer was created should be more than 10 seconds. + UNIT_TEST_CHECK(count == 1); + end = detail::time::now(); + expected_end = start; + expected_end += detail::time(10); + UNIT_TEST_CHECK(expected_end < end); } UNIT_TEST(timer_test)