Add support for timer cancellation.
This commit is contained in:
parent
751785009d
commit
84ae757b75
@ -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 <typename Handler>
|
||||
void async_wait(Handler handler)
|
||||
|
@ -21,8 +21,10 @@
|
||||
#include <boost/noncopyable.hpp>
|
||||
#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();
|
||||
}
|
||||
|
||||
|
@ -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<void*, timer_base*>::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:
|
||||
|
@ -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:
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
void print()
|
||||
void print(const asio::error& /*e*/)
|
||||
{
|
||||
std::cout << "Hello, world!\n";
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -158,13 +158,21 @@ from the whole-second mark due to any delays in processing the handler.
|
||||
<b>Step 4.</b> 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 <tt>void()</tt>.
|
||||
Binding the additional parameters converts your <tt>print</tt> function into a
|
||||
function object that matches the signature correctly.
|
||||
handler function (or function object) with the signature <tt>void(const
|
||||
asio::error&)</tt>. Binding the additional parameters converts your
|
||||
<tt>print</tt> function into a function object that matches the signature
|
||||
correctly.
|
||||
|
||||
See the <a href="http://www.boost.org/libs/bind/bind.html">Boost: bind.hpp
|
||||
documentation</a> 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
|
||||
|
||||
<b>Step 5.</b> A new <tt>count</tt> 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 <tt>this</tt> parameter, we need to bind
|
||||
<tt>this</tt> 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 <tt>void()</tt>.
|
||||
that can be invoked as though it has the signature <tt>void(const
|
||||
asio::error&)</tt>.
|
||||
|
||||
You will note that the asio::arg::error placeholder is not specified here, as
|
||||
the <tt>print</tt> member function does not accept an error object as a
|
||||
parameter.
|
||||
|
||||
\until }
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user