// // compose.cpp // ~~~~~~~~~~~ // // Copyright (c) 2003-2023 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) // Test that header file is self-contained. #include #include "unit_test.hpp" #include #include #include #include #include #if defined(BOOST_ASIO_HAS_BOOST_BIND) # include #else // defined(BOOST_ASIO_HAS_BOOST_BIND) # include #endif // defined(BOOST_ASIO_HAS_BOOST_BIND) //------------------------------------------------------------------------------ class impl_0_completion_args { public: explicit impl_0_completion_args(boost::asio::io_context& ioc) : ioc_(ioc), state_(starting) { } template void operator()(Self& self) { switch (state_) { case starting: state_ = posting; boost::asio::post(ioc_, BOOST_ASIO_MOVE_CAST(Self)(self)); break; case posting: self.complete(); break; default: break; } } private: boost::asio::io_context& ioc_; enum { starting, posting } state_; }; template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) async_0_completion_args(boost::asio::io_context& ioc, BOOST_ASIO_MOVE_ARG(CompletionToken) token) { return boost::asio::async_compose( impl_0_completion_args(ioc), token); } void compose_0_args_handler(int* count) { ++(*count); } struct compose_0_args_lvalue_handler { int* count_; void operator()() { ++(*count_); } }; void compose_0_completion_args_test() { #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) boost::asio::io_context ioc; int count = 0; async_0_completion_args(ioc, bindns::bind(&compose_0_args_handler, &count)); // No handlers can be called until run() is called. BOOST_ASIO_CHECK(!ioc.stopped()); BOOST_ASIO_CHECK(count == 0); ioc.run(); // The run() call will not return until all work has finished. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); ioc.restart(); count = 0; compose_0_args_lvalue_handler lvalue_handler = { &count }; async_0_completion_args(ioc, lvalue_handler); // No handlers can be called until run() is called. BOOST_ASIO_CHECK(!ioc.stopped()); BOOST_ASIO_CHECK(count == 0); ioc.run(); // The run() call will not return until all work has finished. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); } //------------------------------------------------------------------------------ class impl_1_completion_arg { public: explicit impl_1_completion_arg(boost::asio::io_context& ioc) : ioc_(ioc), state_(starting) { } template void operator()(Self& self) { switch (state_) { case starting: state_ = posting; boost::asio::post(ioc_, BOOST_ASIO_MOVE_CAST(Self)(self)); break; case posting: self.complete(42); break; default: break; } } private: boost::asio::io_context& ioc_; enum { starting, posting } state_; }; template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(int)) async_1_completion_arg(boost::asio::io_context& ioc, BOOST_ASIO_MOVE_ARG(CompletionToken) token) { return boost::asio::async_compose( impl_1_completion_arg(ioc), token); } void compose_1_arg_handler(int* count, int* result_out, int result) { ++(*count); *result_out = result; } struct compose_1_arg_lvalue_handler { int* count_; int* result_out_; void operator()(int result) { ++(*count_); *result_out_ = result; } }; void compose_1_completion_arg_test() { #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) using bindns::placeholders::_1; boost::asio::io_context ioc; int count = 0; int result = 0; async_1_completion_arg(ioc, bindns::bind(&compose_1_arg_handler, &count, &result, _1)); // No handlers can be called until run() is called. BOOST_ASIO_CHECK(!ioc.stopped()); BOOST_ASIO_CHECK(count == 0); BOOST_ASIO_CHECK(result == 0); ioc.run(); // The run() call will not return until all work has finished. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == 42); ioc.restart(); count = 0; result = 0; compose_1_arg_lvalue_handler lvalue_handler = { &count, &result }; async_1_completion_arg(ioc, lvalue_handler); // No handlers can be called until run() is called. BOOST_ASIO_CHECK(!ioc.stopped()); BOOST_ASIO_CHECK(count == 0); BOOST_ASIO_CHECK(result == 0); ioc.run(); // The run() call will not return until all work has finished. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == 42); } //------------------------------------------------------------------------------ typedef boost::asio::enable_terminal_cancellation default_filter; template class impl_cancellable { public: explicit impl_cancellable(CancellationFilter cancellation_filter, boost::asio::system_timer& timer) : cancellation_filter_(cancellation_filter), timer_(timer), state_(starting) { } template void operator()(Self& self, const boost::system::error_code& ec = boost::system::error_code()) { switch (state_) { case starting: if (!boost::asio::is_same::value) self.reset_cancellation_state(cancellation_filter_); state_ = waiting; timer_.expires_after(boost::asio::chrono::milliseconds(100)); timer_.async_wait(BOOST_ASIO_MOVE_CAST(Self)(self)); break; case waiting: self.complete(!ec); break; default: break; } } private: CancellationFilter cancellation_filter_; boost::asio::system_timer& timer_; enum { starting, waiting } state_; }; template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(bool)) async_cancellable(CancellationFilter cancellation_filter, boost::asio::system_timer& timer, BOOST_ASIO_MOVE_ARG(CompletionToken) token) { return boost::asio::async_compose( impl_cancellable(cancellation_filter, timer), token); } void compose_partial_cancellation_handler( int* count, bool* result_out, bool result) { ++(*count); *result_out = result; } void compose_default_cancellation_test() { #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) using bindns::placeholders::_1; boost::asio::io_context ioc; boost::asio::system_timer timer(ioc); boost::asio::cancellation_signal signal; int count = 0; bool result = false; async_cancellable(default_filter(), timer, bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1)); ioc.run(); // No cancellation, operation completes successfully. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(default_filter(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Total cancellation unsupported. Operation completes successfully. signal.emit(boost::asio::cancellation_type::total); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(default_filter(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Partial cancellation unsupported. Operation completes successfully. signal.emit(boost::asio::cancellation_type::partial); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(default_filter(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Terminal cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::terminal); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); } void compose_partial_cancellation_test() { #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) using bindns::placeholders::_1; boost::asio::io_context ioc; boost::asio::system_timer timer(ioc); boost::asio::cancellation_signal signal; int count = 0; bool result = false; async_cancellable(boost::asio::enable_partial_cancellation(), timer, bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1)); ioc.run(); // No cancellation, operation completes successfully. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_partial_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Total cancellation unsupported. Operation completes successfully. signal.emit(boost::asio::cancellation_type::total); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_partial_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Partial cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::partial); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_partial_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Terminal cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::terminal); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); } void compose_total_cancellation_test() { #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) using bindns::placeholders::_1; boost::asio::io_context ioc; boost::asio::system_timer timer(ioc); boost::asio::cancellation_signal signal; int count = 0; bool result = false; async_cancellable(boost::asio::enable_total_cancellation(), timer, bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1)); ioc.run(); // No cancellation, operation completes successfully. BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == true); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_total_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Total cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::total); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_total_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Partial cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::partial); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); ioc.restart(); count = 0; result = 0; async_cancellable(boost::asio::enable_total_cancellation(), timer, boost::asio::bind_cancellation_slot(signal.slot(), bindns::bind(&compose_partial_cancellation_handler, &count, &result, _1))); // Terminal cancellation works. Operation completes with failure. signal.emit(boost::asio::cancellation_type::terminal); ioc.run(); BOOST_ASIO_CHECK(ioc.stopped()); BOOST_ASIO_CHECK(count == 1); BOOST_ASIO_CHECK(result == false); } //------------------------------------------------------------------------------ BOOST_ASIO_TEST_SUITE ( "compose", BOOST_ASIO_TEST_CASE(compose_0_completion_args_test) BOOST_ASIO_TEST_CASE(compose_1_completion_arg_test) BOOST_ASIO_TEST_CASE(compose_default_cancellation_test) BOOST_ASIO_TEST_CASE(compose_partial_cancellation_test) BOOST_ASIO_TEST_CASE(compose_total_cancellation_test) )