123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- //
- // system_timer.cpp
- // ~~~~~~~~~~~~~~~~
- //
- // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Disable autolinking for unit tests.
- #if !defined(BOOST_ALL_NO_LIB)
- #define BOOST_ALL_NO_LIB 1
- #endif // !defined(BOOST_ALL_NO_LIB)
- // Prevent link dependency on the Boost.System library.
- #if !defined(BOOST_SYSTEM_NO_DEPRECATED)
- #define BOOST_SYSTEM_NO_DEPRECATED
- #endif // !defined(BOOST_SYSTEM_NO_DEPRECATED)
- // Test that header file is self-contained.
- #include <boost/asio/system_timer.hpp>
- #include "unit_test.hpp"
- #if defined(BOOST_ASIO_HAS_STD_CHRONO)
- #include <boost/asio/executor_work_guard.hpp>
- #include <boost/asio/io_context.hpp>
- #include <boost/asio/detail/thread.hpp>
- #if defined(BOOST_ASIO_HAS_BOOST_BIND)
- # include <boost/bind.hpp>
- #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
- # include <functional>
- #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
- #if defined(BOOST_ASIO_HAS_BOOST_BIND)
- namespace bindns = boost;
- #else // defined(BOOST_ASIO_HAS_BOOST_BIND)
- namespace bindns = std;
- #endif // defined(BOOST_ASIO_HAS_BOOST_BIND)
- void increment(int* count)
- {
- ++(*count);
- }
- void decrement_to_zero(boost::asio::system_timer* t, int* count)
- {
- if (*count > 0)
- {
- --(*count);
- int before_value = *count;
- t->expires_at(t->expiry() + boost::asio::chrono::seconds(1));
- t->async_wait(bindns::bind(decrement_to_zero, t, count));
- // Completion cannot nest, so count value should remain unchanged.
- BOOST_ASIO_CHECK(*count == before_value);
- }
- }
- void increment_if_not_cancelled(int* count,
- const boost::system::error_code& ec)
- {
- if (!ec)
- ++(*count);
- }
- void cancel_timer(boost::asio::system_timer* t)
- {
- std::size_t num_cancelled = t->cancel();
- BOOST_ASIO_CHECK(num_cancelled == 1);
- }
- void cancel_one_timer(boost::asio::system_timer* t)
- {
- std::size_t num_cancelled = t->cancel_one();
- BOOST_ASIO_CHECK(num_cancelled == 1);
- }
- boost::asio::system_timer::time_point now()
- {
- return boost::asio::system_timer::clock_type::now();
- }
- void system_timer_test()
- {
- using boost::asio::chrono::seconds;
- using boost::asio::chrono::microseconds;
- #if !defined(BOOST_ASIO_HAS_BOOST_BIND)
- using std::placeholders::_1;
- using std::placeholders::_2;
- #endif // !defined(BOOST_ASIO_HAS_BOOST_BIND)
- boost::asio::io_context ioc;
- const boost::asio::io_context::executor_type ioc_ex = ioc.get_executor();
- int count = 0;
- boost::asio::system_timer::time_point start = now();
- boost::asio::system_timer t1(ioc, seconds(1));
- t1.wait();
- // The timer must block until after its expiry time.
- boost::asio::system_timer::time_point end = now();
- boost::asio::system_timer::time_point expected_end = start + seconds(1);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- start = now();
- boost::asio::system_timer t2(ioc_ex, seconds(1) + microseconds(500000));
- t2.wait();
- // The timer must block until after its expiry time.
- end = now();
- expected_end = start + seconds(1) + microseconds(500000);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- t2.expires_at(t2.expiry() + seconds(1));
- t2.wait();
- // The timer must block until after its expiry time.
- end = now();
- expected_end += seconds(1);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- start = now();
- t2.expires_after(seconds(1) + microseconds(200000));
- t2.wait();
- // The timer must block until after its expiry time.
- end = now();
- expected_end = start + seconds(1) + microseconds(200000);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- start = now();
- boost::asio::system_timer t3(ioc, seconds(5));
- t3.async_wait(bindns::bind(increment, &count));
- // No completions can be delivered until run() is called.
- BOOST_ASIO_CHECK(count == 0);
- ioc.run();
- // The run() call will not return until all operations have finished, and
- // this should not be until after the timer's expiry time.
- BOOST_ASIO_CHECK(count == 1);
- end = now();
- expected_end = start + seconds(1);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- count = 3;
- start = now();
- boost::asio::system_timer t4(ioc, seconds(1));
- t4.async_wait(bindns::bind(decrement_to_zero, &t4, &count));
- // No completions can be delivered until run() is called.
- BOOST_ASIO_CHECK(count == 3);
- ioc.restart();
- ioc.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.
- BOOST_ASIO_CHECK(count == 0);
- end = now();
- expected_end = start + seconds(3);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- count = 0;
- start = now();
- boost::asio::system_timer t5(ioc, seconds(10));
- t5.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1));
- boost::asio::system_timer t6(ioc, seconds(1));
- t6.async_wait(bindns::bind(cancel_timer, &t5));
- // No completions can be delivered until run() is called.
- BOOST_ASIO_CHECK(count == 0);
- ioc.restart();
- ioc.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).
- BOOST_ASIO_CHECK(count == 0);
- end = now();
- expected_end = start + seconds(2);
- BOOST_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));
- ioc.restart();
- ioc.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.
- BOOST_ASIO_CHECK(count == 1);
- end = now();
- expected_end = start + seconds(10);
- BOOST_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.
- boost::asio::system_timer t7(ioc, seconds(3));
- t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1));
- t7.async_wait(bindns::bind(increment_if_not_cancelled, &count, _1));
- boost::asio::system_timer t8(ioc, seconds(1));
- t8.async_wait(bindns::bind(cancel_one_timer, &t7));
- ioc.restart();
- ioc.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.
- BOOST_ASIO_CHECK(count == 1);
- end = now();
- expected_end = start + seconds(3);
- BOOST_ASIO_CHECK(expected_end < end || expected_end == end);
- }
- struct timer_handler
- {
- timer_handler() {}
- void operator()(const boost::system::error_code&) {}
- #if defined(BOOST_ASIO_HAS_MOVE)
- timer_handler(timer_handler&&) {}
- private:
- timer_handler(const timer_handler&);
- #endif // defined(BOOST_ASIO_HAS_MOVE)
- };
- void system_timer_cancel_test()
- {
- static boost::asio::io_context io_context;
- struct timer
- {
- boost::asio::system_timer t;
- timer() : t(io_context)
- {
- t.expires_at((boost::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());
- BOOST_ASIO_CHECK(timers[2].t.cancel() == 1);
- BOOST_ASIO_CHECK(timers[41].t.cancel() == 1);
- for (int i = 10; i < 20; ++i)
- BOOST_ASIO_CHECK(timers[i].t.cancel() == 1);
- }
- struct custom_allocation_timer_handler
- {
- custom_allocation_timer_handler(int* count) : count_(count) {}
- void operator()(const boost::system::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 boost::asio::io_context io_context;
- struct timer
- {
- boost::asio::system_timer t;
- timer() : t(io_context) {}
- } timers[100];
- int allocation_count = 0;
- for (int i = 0; i < 50; ++i)
- {
- timers[i].t.expires_at((boost::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((boost::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_context.run();
- BOOST_ASIO_CHECK(allocation_count == 0);
- }
- void io_context_run(boost::asio::io_context* ioc)
- {
- ioc->run();
- }
- void system_timer_thread_test()
- {
- boost::asio::io_context ioc;
- boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work
- = boost::asio::make_work_guard(ioc);
- boost::asio::system_timer t1(ioc);
- boost::asio::system_timer t2(ioc);
- int count = 0;
- boost::asio::detail::thread th(bindns::bind(io_context_run, &ioc));
- t2.expires_after(boost::asio::chrono::seconds(2));
- t2.wait();
- t1.expires_after(boost::asio::chrono::seconds(2));
- t1.async_wait(bindns::bind(increment, &count));
- t2.expires_after(boost::asio::chrono::seconds(4));
- t2.wait();
- ioc.stop();
- th.join();
- BOOST_ASIO_CHECK(count == 1);
- }
- #if defined(BOOST_ASIO_HAS_MOVE)
- boost::asio::system_timer make_timer(boost::asio::io_context& ioc, int* count)
- {
- boost::asio::system_timer t(ioc);
- t.expires_after(boost::asio::chrono::seconds(1));
- t.async_wait(bindns::bind(increment, count));
- return t;
- }
- #endif
- void system_timer_move_test()
- {
- #if defined(BOOST_ASIO_HAS_MOVE)
- boost::asio::io_context io_context1;
- boost::asio::io_context io_context2;
- int count = 0;
- boost::asio::system_timer t1 = make_timer(io_context1, &count);
- boost::asio::system_timer t2 = make_timer(io_context2, &count);
- boost::asio::system_timer t3 = std::move(t1);
- t2 = std::move(t1);
- io_context2.run();
- BOOST_ASIO_CHECK(count == 1);
- io_context1.run();
- BOOST_ASIO_CHECK(count == 2);
- #endif // defined(BOOST_ASIO_HAS_MOVE)
- }
- BOOST_ASIO_TEST_SUITE
- (
- "system_timer",
- BOOST_ASIO_TEST_CASE(system_timer_test)
- BOOST_ASIO_TEST_CASE(system_timer_cancel_test)
- BOOST_ASIO_TEST_CASE(system_timer_custom_allocation_test)
- BOOST_ASIO_TEST_CASE(system_timer_thread_test)
- BOOST_ASIO_TEST_CASE(system_timer_move_test)
- )
- #else // defined(BOOST_ASIO_HAS_STD_CHRONO)
- BOOST_ASIO_TEST_SUITE
- (
- "system_timer",
- BOOST_ASIO_TEST_CASE(null_test)
- )
- #endif // defined(BOOST_ASIO_HAS_STD_CHRONO)
|