Add test for system_timer when using std::chrono.
diff --git a/asio/src/tests/unit/system_timer.cpp b/asio/src/tests/unit/system_timer.cpp index a20c08a..1525f90 100644 --- a/asio/src/tests/unit/system_timer.cpp +++ b/asio/src/tests/unit/system_timer.cpp
@@ -23,8 +23,337 @@ #include "unit_test.hpp" +#if defined(ASIO_HAS_STD_CHRONO) + +#include "asio/io_service.hpp" +#include "asio/thread.hpp" + +#if defined(ASIO_HAS_BOOST_BIND) +# include <boost/bind.hpp> +#else // defined(ASIO_HAS_BOOST_BIND) +# include <functional> +#endif // defined(ASIO_HAS_BOOST_BIND) + +#if defined(ASIO_HAS_BOOST_BIND) +namespace bindns = boost; +#else // defined(ASIO_HAS_BOOST_BIND) +namespace bindns = std; +#endif // defined(ASIO_HAS_BOOST_BIND) + +namespace chronons = std::chrono; + +void increment(int* count) +{ + ++(*count); +} + +void decrement_to_zero(asio::system_timer* t, int* count) +{ + if (*count > 0) + { + --(*count); + + int before_value = *count; + + t->expires_at(t->expires_at() + chronons::seconds(1)); + t->async_wait(bindns::bind(decrement_to_zero, t, count)); + + // Completion cannot nest, so count value should remain unchanged. + ASIO_CHECK(*count == before_value); + } +} + +void increment_if_not_cancelled(int* count, + const asio::error_code& ec) +{ + if (!ec) + ++(*count); +} + +void cancel_timer(asio::system_timer* t) +{ + std::size_t num_cancelled = t->cancel(); + ASIO_CHECK(num_cancelled == 1); +} + +void cancel_one_timer(asio::system_timer* t) +{ + std::size_t num_cancelled = t->cancel_one(); + ASIO_CHECK(num_cancelled == 1); +} + +asio::system_timer::time_point now() +{ + return asio::system_timer::clock_type::now(); +} + +void system_timer_test() +{ + using chronons::seconds; + using chronons::microseconds; +#if !defined(ASIO_HAS_BOOST_BIND) + using std::placeholders::_1; + using std::placeholders::_2; +#endif // !defined(ASIO_HAS_BOOST_BIND) + + asio::io_service ios; + int count = 0; + + asio::system_timer::time_point start = now(); + + asio::system_timer t1(ios, seconds(1)); + t1.wait(); + + // The timer must block until after its expiry time. + asio::system_timer::time_point end = now(); + asio::system_timer::time_point expected_end = start + seconds(1); + ASIO_CHECK(expected_end < end || expected_end == end); + + start = now(); + + asio::system_timer t2(ios, seconds(1) + microseconds(500000)); + t2.wait(); + + // The timer must block until after its expiry time. + end = now(); + expected_end = start + seconds(1) + microseconds(500000); + ASIO_CHECK(expected_end < end || expected_end == end); + + t2.expires_at(t2.expires_at() + seconds(1)); + t2.wait(); + + // The timer must block until after its expiry time. + end = now(); + expected_end += seconds(1); + ASIO_CHECK(expected_end < end || expected_end == end); + + start = now(); + + t2.expires_from_now(seconds(1) + microseconds(200000)); + t2.wait(); + + // The timer must block until after its expiry time. + end = now(); + expected_end = start + seconds(1) + microseconds(200000); + ASIO_CHECK(expected_end < end || expected_end == end); + + start = now(); + + asio::system_timer t3(ios, seconds(5)); + t3.async_wait(bindns::bind(increment, &count)); + + // No completions can be delivered until run() is called. + ASIO_CHECK(count == 0); + + ios.run(); + + // The run() call will not return until all operations have finished, and + // this should not be until after the timer's expiry time. + ASIO_CHECK(count == 1); + end = now(); + expected_end = start + seconds(1); + ASIO_CHECK(expected_end < end || expected_end == end); + + count = 3; + start = now(); + + asio::system_timer t4(ios, seconds(1)); + t4.async_wait(bindns::bind(decrement_to_zero, &t4, &count)); + + // No completions can be delivered until run() is called. + ASIO_CHECK(count == 3); + + ios.reset(); + ios.run(); + + // The run() call will not return until all operations have finished, and + // this should not be until after the timer's final expiry time. + ASIO_CHECK(count == 0); + end = now(); + expected_end = start + seconds(3); + ASIO_CHECK(expected_end < end || expected_end == end); + + count = 0; + start = now(); + + asio::system_timer t5(ios, seconds(10)); + t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); + asio::system_timer t6(ios, seconds(1)); + t6.async_wait(bindns::bind(cancel_timer, &t5)); + + // No completions can be delivered until run() is called. + ASIO_CHECK(count == 0); + + ios.reset(); + ios.run(); + + // 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 10 seconds). + ASIO_CHECK(count == 0); + end = now(); + expected_end = start + seconds(2); + ASIO_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(bindns::bind(increment_if_not_cancelled, &count, _1)); + + ios.reset(); + ios.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. + ASIO_CHECK(count == 1); + end = now(); + expected_end = start + seconds(10); + ASIO_CHECK(expected_end < end || expected_end == end); + + count = 0; + start = now(); + + // Start two waits on a timer, one of which will be cancelled. The one + // which is not cancelled should still run to completion and increment the + // counter. + asio::system_timer t7(ios, seconds(3)); + t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); + t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1)); + asio::system_timer t8(ios, seconds(1)); + t8.async_wait(bindns::bind(cancel_one_timer, &t7)); + + ios.reset(); + ios.run(); + + // One of the waits should not have been cancelled, so count should have + // changed. The total time since the timer was created should be more than 3 + // seconds. + ASIO_CHECK(count == 1); + end = now(); + expected_end = start + seconds(3); + ASIO_CHECK(expected_end < end || expected_end == end); +} + +void timer_handler(const asio::error_code&) +{ +} + +void system_timer_cancel_test() +{ + static asio::io_service io_service; + struct timer + { + asio::system_timer t; + timer() : t(io_service) + { + t.expires_at(asio::system_timer::time_point::max()); + } + } timers[50]; + + timers[2].t.async_wait(&timer_handler); + timers[41].t.async_wait(&timer_handler); + for (int i = 10; i < 20; ++i) + timers[i].t.async_wait(&timer_handler); + + ASIO_CHECK(timers[2].t.cancel() == 1); + ASIO_CHECK(timers[41].t.cancel() == 1); + for (int i = 10; i < 20; ++i) + ASIO_CHECK(timers[i].t.cancel() == 1); +} + +struct custom_allocation_timer_handler +{ + custom_allocation_timer_handler(int* count) : count_(count) {} + void operator()(const asio::error_code&) {} + int* count_; +}; + +void* asio_handler_allocate(std::size_t size, + custom_allocation_timer_handler* handler) +{ + ++(*handler->count_); + return ::operator new(size); +} + +void asio_handler_deallocate(void* pointer, std::size_t, + custom_allocation_timer_handler* handler) +{ + --(*handler->count_); + ::operator delete(pointer); +} + +void system_timer_custom_allocation_test() +{ + static asio::io_service io_service; + struct timer + { + asio::system_timer t; + timer() : t(io_service) {} + } timers[100]; + + int allocation_count = 0; + + for (int i = 0; i < 50; ++i) + { + timers[i].t.expires_at(asio::system_timer::time_point::max()); + timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); + } + + for (int i = 50; i < 100; ++i) + { + timers[i].t.expires_at(asio::system_timer::time_point::min()); + timers[i].t.async_wait(custom_allocation_timer_handler(&allocation_count)); + } + + for (int i = 0; i < 50; ++i) + timers[i].t.cancel(); + + io_service.run(); + + ASIO_CHECK(allocation_count == 0); +} + +void io_service_run(asio::io_service* ios) +{ + ios->run(); +} + +void system_timer_thread_test() +{ + asio::io_service ios; + asio::io_service::work w(ios); + asio::system_timer t1(ios); + asio::system_timer t2(ios); + int count = 0; + + asio::thread th(bindns::bind(io_service_run, &ios)); + + t2.expires_from_now(chronons::seconds(2)); + t2.wait(); + + t1.expires_from_now(chronons::seconds(2)); + t1.async_wait(bindns::bind(increment, &count)); + + t2.expires_from_now(chronons::seconds(4)); + t2.wait(); + + ios.stop(); + th.join(); + + ASIO_CHECK(count == 1); +} + +ASIO_TEST_SUITE +( + "system_timer", + ASIO_TEST_CASE(system_timer_test) + ASIO_TEST_CASE(system_timer_cancel_test) + ASIO_TEST_CASE(system_timer_custom_allocation_test) + ASIO_TEST_CASE(system_timer_thread_test) +) +#else // defined(ASIO_HAS_STD_CHRONO) ASIO_TEST_SUITE ( "system_timer", ASIO_TEST_CASE(null_test) ) +#endif // defined(ASIO_HAS_STD_CHRONO)