Add support for timer cancellation.

This commit is contained in:
chris 2004-08-27 07:32:06 +00:00
parent 751785009d
commit 84ae757b75
8 changed files with 79 additions and 38 deletions

View File

@ -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)

View File

@ -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();
}

View File

@ -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:

View File

@ -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:

View File

@ -1,7 +1,7 @@
#include <iostream>
#include "asio.hpp"
void print()
void print(const asio::error& /*e*/)
{
std::cout << "Hello, world!\n";
}

View File

@ -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();

View File

@ -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 }

View File

@ -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)