diff --git a/include/boost/mysql/detail/connection_impl.hpp b/include/boost/mysql/detail/connection_impl.hpp index 99f917ccf..119dfef46 100644 --- a/include/boost/mysql/detail/connection_impl.hpp +++ b/include/boost/mysql/detail/connection_impl.hpp @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include #include @@ -121,37 +121,16 @@ class connection_impl BOOST_MYSQL_DECL static std::vector& get_shared_fields(connection_state& st) noexcept; - // Utilities for standard network algorithms - struct generic_initiation + // Generic algorithm + struct run_algo_initiation { template void operator()(Handler&& handler, any_stream* stream, connection_state* st, OpParams params) { - async_generic_algo(*stream, *st, params, std::forward(handler)); + async_run_algo(*stream, *st, params, std::forward(handler)); } }; - // connect - BOOST_MYSQL_DECL - static void connect_erased( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - error_code& err, - diagnostics& diag - ); - - BOOST_MYSQL_DECL - static void async_connect_erased( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - diagnostics& diag, - any_void_handler handler - ); - template struct connect_initiation { @@ -165,7 +144,12 @@ class connection_impl diagnostics* diag ) { - async_connect_erased(*stream, *st, &endpoint, params, *diag, std::forward(handler)); + async_run_algo( + *stream, + *st, + connect_op_params{&endpoint, diag, params, stream->supports_ssl()}, + std::forward(handler) + ); } }; @@ -183,7 +167,7 @@ class connection_impl ) { auto getter = make_request_getter(req, get_shared_fields(*st)); - async_generic_algo( + async_run_algo( *stream, *st, execute_op_params{diag, getter.get(), proc}, @@ -206,7 +190,7 @@ class connection_impl ) { auto getter = make_request_getter(req, get_shared_fields(*st)); - async_generic_algo( + async_run_algo( *stream, *st, start_execution_op_params{diag, getter.get(), proc}, @@ -245,7 +229,7 @@ class connection_impl async_generic_algo_iface(OpParams params, CompletionToken&& token) { return asio::async_initiate>( - generic_initiation(), + run_algo_initiation(), token, stream_.get(), st_.get(), @@ -256,7 +240,7 @@ class connection_impl template typename OpParams::result_type generic_algo_iface(OpParams params, error_code& ec) { - return generic_algo(*stream_, *st_, params, ec); + return run_algo(*stream_, *st_, params, ec); } // Connect. This handles casting to the corresponding endpoint_type, if required @@ -268,7 +252,7 @@ class connection_impl diagnostics& diag ) { - connect_erased(*stream_, *st_, &ep, params, err, diag); + run_algo(*stream_, *st_, connect_op_params{&ep, &diag, params, stream_->supports_ssl()}, err); } template @@ -303,7 +287,7 @@ class connection_impl void execute(const ExecutionRequest& req, ResultsType& result, error_code& err, diagnostics& diag) { auto getter = make_request_getter(req, get_shared_fields(*st_)); - generic_algo( + run_algo( *stream_, *st_, execute_op_params{&diag, getter.get(), &access::get_impl(result).get_interface()}, @@ -336,7 +320,7 @@ class connection_impl ) { auto getter = make_request_getter(req, get_shared_fields(*st_)); - generic_algo( + run_algo( *stream_, *st_, start_execution_op_params{&diag, getter.get(), &access::get_impl(exec_st).get_interface()}, @@ -423,7 +407,7 @@ class connection_impl } // namespace boost #ifdef BOOST_MYSQL_HEADER_ONLY -#include +#include #endif #endif diff --git a/include/boost/mysql/detail/network_algorithm_traits.hpp b/include/boost/mysql/detail/network_algorithm_traits.hpp index f551bc2d7..dc2ae1828 100644 --- a/include/boost/mysql/detail/network_algorithm_traits.hpp +++ b/include/boost/mysql/detail/network_algorithm_traits.hpp @@ -25,6 +25,16 @@ namespace boost { namespace mysql { namespace detail { +struct connect_op_params +{ + const void* connect_arg; + diagnostics* diag; + handshake_params hparams; + bool transport_supports_ssl; + + using result_type = void; +}; + struct handshake_op_params { diagnostics* diag; diff --git a/include/boost/mysql/detail/generic_algo.hpp b/include/boost/mysql/detail/run_algo.hpp similarity index 88% rename from include/boost/mysql/detail/generic_algo.hpp rename to include/boost/mysql/detail/run_algo.hpp index 4f908944f..c0f231d40 100644 --- a/include/boost/mysql/detail/generic_algo.hpp +++ b/include/boost/mysql/detail/run_algo.hpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_MYSQL_DETAIL_GENERIC_ALGO_HPP -#define BOOST_MYSQL_DETAIL_GENERIC_ALGO_HPP +#ifndef BOOST_MYSQL_DETAIL_RUN_ALGO_HPP +#define BOOST_MYSQL_DETAIL_RUN_ALGO_HPP #include @@ -47,7 +47,7 @@ template using completion_handler_t = asio::any_completion_handler>; template -typename OpParams::result_type generic_algo( +typename OpParams::result_type run_algo( any_stream& stream, connection_state& st, OpParams params, @@ -55,7 +55,7 @@ typename OpParams::result_type generic_algo( ); template -void async_generic_algo( +void async_run_algo( any_stream& stream, connection_state& st, OpParams params, @@ -67,7 +67,7 @@ void async_generic_algo( } // namespace boost #ifdef BOOST_MYSQL_HEADER_ONLY -#include +#include #endif #endif diff --git a/include/boost/mysql/impl/connection_impl.ipp b/include/boost/mysql/impl/connection_impl.ipp index a91192e51..bd44ab354 100644 --- a/include/boost/mysql/impl/connection_impl.ipp +++ b/include/boost/mysql/impl/connection_impl.ipp @@ -13,12 +13,10 @@ #include #include -#include #include #include -// Misc std::vector& boost::mysql::detail::connection_impl::get_shared_fields( connection_state& st ) noexcept @@ -26,31 +24,6 @@ std::vector& boost::mysql::detail::connection_impl::ge return st.data().shared_fields; } -// connect -void boost::mysql::detail::connection_impl::connect_erased( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - error_code& err, - diagnostics& diag -) -{ - connect_impl(stream, st, endpoint, params, err, diag); -} - -void boost::mysql::detail::connection_impl::async_connect_erased( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - diagnostics& diag, - any_void_handler handler -) -{ - async_connect_impl(stream, st, endpoint, params, diag, std::move(handler)); -} - boost::mysql::detail::connection_impl::connection_impl( std::size_t read_buff_size, std::unique_ptr stream diff --git a/include/boost/mysql/impl/generic_algo.ipp b/include/boost/mysql/impl/generic_algo.ipp deleted file mode 100644 index fb2b2d6f4..000000000 --- a/include/boost/mysql/impl/generic_algo.ipp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) -// - -#ifndef BOOST_MYSQL_IMPL_GENERIC_ALGO_IPP -#define BOOST_MYSQL_IMPL_GENERIC_ALGO_IPP - -#pragma once - -#include -#include -#include - -#include - -template -typename OpParams::result_type boost::mysql::detail::generic_algo( - any_stream& stream, - connection_state& st, - OpParams params, - error_code& ec -) -{ - return generic_algo_impl(stream, st, params, ec); -} - -template -void boost::mysql::detail::async_generic_algo( - any_stream& stream, - connection_state& st, - OpParams params, - completion_handler_t handler -) -{ - async_generic_algo_impl(stream, st, params, std::move(handler)); -} - -#ifdef BOOST_MYSQL_SEPARATE_COMPILATION - -#define BOOST_MYSQL_INSTANTIATE_ALGO(op_params_type) \ - template op_params_type::result_type \ - generic_algo(any_stream&, connection_state&, op_params_type, error_code&); \ - template void async_generic_algo< \ - op_params_type>(any_stream&, connection_state&, op_params_type, completion_handler_t); - -namespace boost { -namespace mysql { -namespace detail { - -BOOST_MYSQL_INSTANTIATE_ALGO(handshake_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(execute_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(start_execution_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(read_resultset_head_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(read_some_rows_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(read_some_rows_dynamic_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(prepare_statement_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(close_statement_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(ping_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(reset_connection_op_params) -BOOST_MYSQL_INSTANTIATE_ALGO(quit_connection_op_params) - -} // namespace detail -} // namespace mysql -} // namespace boost - -#endif - -#endif diff --git a/include/boost/mysql/impl/internal/network_algorithms/connect.hpp b/include/boost/mysql/impl/internal/network_algorithms/connect.hpp deleted file mode 100644 index 3f56e8d71..000000000 --- a/include/boost/mysql/impl/internal/network_algorithms/connect.hpp +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) -// - -#ifndef BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_HPP -#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_HPP - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -namespace boost { -namespace mysql { -namespace detail { - -struct connect_op : boost::asio::coroutine -{ - any_stream& stream_; - connection_state& st_; - diagnostics& diag_; - const void* ep_; - handshake_params params_; - - connect_op( - any_stream& stream, - connection_state& st, - diagnostics& diag, - const void* ep, - const handshake_params& params - ) - : stream_(stream), st_(st), diag_(diag), ep_(ep), params_(params) - { - } - - template - void operator()(Self& self, error_code code = {}) - { - error_code ignored; - BOOST_ASIO_CORO_REENTER(*this) - { - diag_.clear(); - - // Physical connect - BOOST_ASIO_CORO_YIELD stream_.async_connect(ep_, std::move(self)); - if (code) - { - stream_.close(ignored); - self.complete(code); - BOOST_ASIO_CORO_YIELD break; - } - - // Handshake - BOOST_ASIO_CORO_YIELD async_generic_algo( - stream_, - st_, - handshake_op_params{&diag_, params_, stream_.supports_ssl()}, - std::move(self) - ); - if (code) - { - stream_.close(ignored); - } - self.complete(code); - } - } -}; - -// External interface -inline void connect_impl( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - error_code& err, - diagnostics& diag -) -{ - err.clear(); - diag.clear(); - - error_code ignored; - stream.connect(endpoint, err); - if (err) - { - stream.close(ignored); - return; - } - generic_algo(stream, st, handshake_op_params{&diag, params, stream.supports_ssl()}, err); - if (err) - { - stream.close(ignored); - } -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) -async_connect_impl( - any_stream& stream, - connection_state& st, - const void* endpoint, - const handshake_params& params, - diagnostics& diag, - CompletionToken&& token -) -{ - return asio::async_compose( - connect_op{stream, st, diag, endpoint, params}, - token, - stream - ); -} - -} // namespace detail -} // namespace mysql -} // namespace boost - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP_ */ diff --git a/include/boost/mysql/impl/internal/network_algorithms/connect_v2.hpp b/include/boost/mysql/impl/internal/network_algorithms/connect_v2.hpp deleted file mode 100644 index 6924b3344..000000000 --- a/include/boost/mysql/impl/internal/network_algorithms/connect_v2.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) -// - -#ifndef BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_V2_HPP -#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_V2_HPP - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -namespace boost { -namespace mysql { -namespace detail { - -// TODO: this -inline void ensure_stream(channel& chan, const server_address_type& addr); - -inline handshake_params get_handshake_params(const connect_params& input) noexcept -{ - return { - input.username, - input.password, - input.database, - input.connection_collation, - input.ssl, - input.multi_queries, - }; -} - -// External interface -inline void connect_v2_impl(channel& chan, const connect_params& params, error_code& err, diagnostics& diag) -{ - error_code ignored; - - err.clear(); - diag.clear(); - - // If the connection is connected, close it, ignoring any errors - if (chan.status() == connection_status::connected) - { - close_connection_impl(chan, err, diag); - err.clear(); - diag.clear(); - } - - // Reset the underlying stream, if required - ensure_stream(chan, params.server_address); - - // Resolve (if required) and connect - chan.stream().connect_v2(params.server_address, err); - if (err) - { - chan.stream().close(ignored); - chan.set_status(connection_status::close); - return; - } - - // Handshake - handshake_impl(chan, get_handshake_params(params), err, diag); - if (err) - { - chan.stream().close(ignored); - chan.set_status(connection_status::close); - } -} - -} // namespace detail -} // namespace mysql -} // namespace boost - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP_ */ diff --git a/include/boost/mysql/impl/internal/network_algorithms/generic_algo.hpp b/include/boost/mysql/impl/internal/network_algorithms/generic_algo.hpp deleted file mode 100644 index 680b91103..000000000 --- a/include/boost/mysql/impl/internal/network_algorithms/generic_algo.hpp +++ /dev/null @@ -1,296 +0,0 @@ -// -// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) -// - -#ifndef BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_GENERIC_ALGO_HPP -#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_GENERIC_ALGO_HPP - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef BOOST_MYSQL_VALGRIND_TESTS -#include -#endif - -namespace boost { -namespace mysql { -namespace detail { - -// Valgrind -#ifdef BOOST_MYSQL_VALGRIND_TESTS -inline void valgrind_make_mem_defined(const void* data, std::size_t size) -{ - VALGRIND_MAKE_MEM_DEFINED(data, size); -} -#else -inline void valgrind_make_mem_defined(const void*, std::size_t) noexcept {} -#endif - -struct generic_op : boost::asio::coroutine -{ - any_stream& stream_; - connection_state& st_; - bool has_done_io_{false}; - error_code stored_ec_; - - generic_op(any_stream& stream, connection_state& st) noexcept : stream_(stream), st_(st) {} - - template - void operator()(Self& self, error_code io_ec = {}, std::size_t bytes_transferred = 0) - { - next_action act; - - BOOST_ASIO_CORO_REENTER(*this) - { - while (true) - { - // Run the op - act = st_.resume(io_ec, bytes_transferred); - if (act.is_done()) - { - stored_ec_ = act.error(); - if (!has_done_io_) - BOOST_ASIO_CORO_YIELD asio::post(stream_.get_executor(), std::move(self)); - self.complete(stored_ec_); - BOOST_ASIO_CORO_YIELD break; - } - else if (act.type() == next_action::type_t::read) - { - BOOST_ASIO_CORO_YIELD stream_.async_read_some(st_.read_buffer(), std::move(self)); - valgrind_make_mem_defined(st_.read_buffer().data(), bytes_transferred); - has_done_io_ = true; - } - else if (act.type() == next_action::type_t::write) - { - BOOST_ASIO_CORO_YIELD stream_.async_write_some(st_.write_buffer(), std::move(self)); - has_done_io_ = true; - } - else if (act.type() == next_action::type_t::ssl_handshake) - { - BOOST_ASIO_CORO_YIELD stream_.async_handshake(std::move(self)); - if (!io_ec) - stream_.set_ssl_active(); - has_done_io_ = true; - } - else if (act.type() == next_action::type_t::ssl_shutdown) - { - if (stream_.ssl_active()) - { - BOOST_ASIO_CORO_YIELD stream_.async_shutdown(std::move(self)); - stream_.set_ssl_torn_down(); - has_done_io_ = true; - } - } - else if (act.type() == next_action::type_t::connect) - { - BOOST_ASIO_CORO_YIELD stream_.async_connect(act.connect_arg(), std::move(self)); - has_done_io_ = true; - } - else - { - BOOST_ASSERT(act.type() == next_action::type_t::close); - stream_.close(io_ec); - } - } - } - } -}; - -inline void generic_algo_without_setup(any_stream& stream, connection_state& st, error_code& ec) -{ - ec.clear(); - error_code io_ec; - std::size_t bytes_transferred = 0; - - while (true) - { - // Run the op - auto act = st.resume(io_ec, bytes_transferred); - - // Apply the next action - bytes_transferred = 0; - if (act.is_done()) - { - ec = act.error(); - return; - } - else if (act.type() == next_action::type_t::read) - { - bytes_transferred = stream.read_some(st.read_buffer(), io_ec); - valgrind_make_mem_defined(st.read_buffer().data(), bytes_transferred); - } - else if (act.type() == next_action::type_t::write) - { - bytes_transferred = stream.write_some(st.write_buffer(), io_ec); - } - else if (act.type() == next_action::type_t::ssl_handshake) - { - stream.handshake(io_ec); - if (!ec) - stream.set_ssl_active(); - } - else if (act.type() == next_action::type_t::ssl_shutdown) - { - if (stream.ssl_active()) - { - stream.shutdown(io_ec); - stream.set_ssl_torn_down(); - } - } - else if (act.type() == next_action::type_t::connect) - { - stream.connect(act.connect_arg(), ec); - } - else - { - BOOST_ASSERT(act.type() == next_action::type_t::close); - stream.close(io_ec); - } - } -} - -template -class generic_algo_handler -{ -public: - static_assert(!has_void_result(), "OpParam::result_type should be non-void"); - - using result_t = typename OpParam::result_type; - - using final_handler_t = asio::any_completion_handler; - generic_algo_handler(final_handler_t&& h, connection_state& st) : final_handler_(std::move(h)), st_(&st) - { - } - - using allocator_type = final_handler_t::allocator_type; - using cancellation_slot_type = final_handler_t::cancellation_slot_type; - - allocator_type get_allocator() const noexcept { return final_handler_.get_allocator(); } - cancellation_slot_type get_cancellation_slot() const noexcept - { - return final_handler_.get_cancellation_slot(); - } - - const auto& handler() const noexcept { return final_handler_; } - - void operator()(error_code ec) - { - std::move(final_handler_)(ec, ec ? result_t{} : st_->result()); - } - -private: - final_handler_t final_handler_; - connection_state* st_; -}; - -template -asio::any_completion_handler make_handler( - completion_handler_t&& final_handler, - connection_state& st, - typename std::enable_if()>::type* = nullptr -) -{ - return generic_algo_handler(std::move(final_handler), st); -} - -template -asio::any_completion_handler make_handler( - asio::any_completion_handler&& final_handler, - connection_state&, - typename std::enable_if()>::type* = nullptr -) -{ - return std::move(final_handler); -} - -template -typename OpParams::result_type generic_algo_impl( - any_stream& stream, - connection_state& st, - OpParams params, - error_code& ec -) -{ - st.setup(params); - generic_algo_without_setup(stream, st, ec); - return st.result(); -} - -template -void async_generic_algo_impl( - any_stream& stream, - connection_state& st, - OpParams params, - completion_handler_t final_handler -) -{ - auto handler = make_handler(std::move(final_handler), st); - st.setup(params); - return asio::async_compose, void(error_code)>( - generic_op{stream, st}, - handler, - stream - ); -} - -} // namespace detail -} // namespace mysql -} // namespace boost - -namespace boost { -namespace asio { - -template -struct associated_executor, Candidate> -{ - using type = any_completion_executor; - - static type get( - const mysql::detail::generic_algo_handler& handler, - const Candidate& candidate = Candidate() - ) noexcept - { - return asio::get_associated_executor(handler.handler(), candidate); - } -}; - -template -struct associated_immediate_executor, Candidate> -{ - using type = any_completion_executor; - - static type get( - const mysql::detail::generic_algo_handler& handler, - const Candidate& candidate = Candidate() - ) BOOST_ASIO_NOEXCEPT - { - return asio::get_associated_immediate_executor(handler.handler(), candidate); - } -}; - -} // namespace asio -} // namespace boost - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP_ */ diff --git a/include/boost/mysql/impl/internal/network_algorithms/run_algo_impl.hpp b/include/boost/mysql/impl/internal/network_algorithms/run_algo_impl.hpp new file mode 100644 index 000000000..3ab6e187b --- /dev/null +++ b/include/boost/mysql/impl/internal/network_algorithms/run_algo_impl.hpp @@ -0,0 +1,166 @@ +// +// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) +// + +#ifndef BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_RUN_ALGO_IMPL_HPP +#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_RUN_ALGO_IMPL_HPP + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace mysql { +namespace detail { + +struct run_algo_op : boost::asio::coroutine +{ + any_stream& stream_; + algo_runner runner_; + bool has_done_io_{false}; + error_code stored_ec_; + + run_algo_op(any_stream& stream, any_algo_ref algo) noexcept : stream_(stream), runner_(algo) {} + + template + void operator()(Self& self, error_code io_ec = {}, std::size_t bytes_transferred = 0) + { + next_action act; + + BOOST_ASIO_CORO_REENTER(*this) + { + while (true) + { + // Run the op + act = runner_.resume(io_ec, bytes_transferred); + if (act.is_done()) + { + stored_ec_ = act.error(); + if (!has_done_io_) + BOOST_ASIO_CORO_YIELD asio::post(stream_.get_executor(), std::move(self)); + self.complete(stored_ec_); + BOOST_ASIO_CORO_YIELD break; + } + else if (act.type() == next_action::type_t::read) + { + BOOST_ASIO_CORO_YIELD stream_.async_read_some(runner_.read_buffer(), std::move(self)); + has_done_io_ = true; + } + else if (act.type() == next_action::type_t::write) + { + BOOST_ASIO_CORO_YIELD stream_.async_write_some(runner_.write_buffer(), std::move(self)); + has_done_io_ = true; + } + else if (act.type() == next_action::type_t::ssl_handshake) + { + BOOST_ASIO_CORO_YIELD stream_.async_handshake(std::move(self)); + if (!io_ec) + stream_.set_ssl_active(); + has_done_io_ = true; + } + else if (act.type() == next_action::type_t::ssl_shutdown) + { + if (stream_.ssl_active()) + { + BOOST_ASIO_CORO_YIELD stream_.async_shutdown(std::move(self)); + stream_.set_ssl_torn_down(); + has_done_io_ = true; + } + } + else if (act.type() == next_action::type_t::connect) + { + BOOST_ASIO_CORO_YIELD stream_.async_connect(act.connect_arg(), std::move(self)); + has_done_io_ = true; + } + else + { + BOOST_ASSERT(act.type() == next_action::type_t::close); + stream_.close(io_ec); + } + } + } + } +}; + +inline void run_algo_impl(any_stream& stream, any_algo_ref algo, error_code& ec) +{ + ec.clear(); + error_code io_ec; + std::size_t bytes_transferred = 0; + algo_runner runner(algo); + + while (true) + { + // Run the op + auto act = runner.resume(io_ec, bytes_transferred); + + // Apply the next action + bytes_transferred = 0; + if (act.is_done()) + { + ec = act.error(); + return; + } + else if (act.type() == next_action::type_t::read) + { + bytes_transferred = stream.read_some(runner.read_buffer(), io_ec); + } + else if (act.type() == next_action::type_t::write) + { + bytes_transferred = stream.write_some(runner.write_buffer(), io_ec); + } + else if (act.type() == next_action::type_t::ssl_handshake) + { + stream.handshake(io_ec); + if (!ec) + stream.set_ssl_active(); + } + else if (act.type() == next_action::type_t::ssl_shutdown) + { + if (stream.ssl_active()) + { + stream.shutdown(io_ec); + stream.set_ssl_torn_down(); + } + } + else if (act.type() == next_action::type_t::connect) + { + stream.connect(act.connect_arg(), ec); + } + else + { + BOOST_ASSERT(act.type() == next_action::type_t::close); + stream.close(io_ec); + } + } +} + +inline void async_run_algo_impl( + any_stream& stream, + any_algo_ref algo, + asio::any_completion_handler handler +) +{ + return asio::async_compose, void(error_code)>( + run_algo_op(stream, algo), + handler, + stream + ); +} + +} // namespace detail +} // namespace mysql +} // namespace boost + +#endif diff --git a/include/boost/mysql/impl/internal/sansio/algo_runner.hpp b/include/boost/mysql/impl/internal/sansio/algo_runner.hpp new file mode 100644 index 000000000..2635cd6a2 --- /dev/null +++ b/include/boost/mysql/impl/internal/sansio/algo_runner.hpp @@ -0,0 +1,123 @@ +// +// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) +// + +#ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_ALGO_RUNNER_HPP +#define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_ALGO_RUNNER_HPP + +#include + +#include +#include +#include + +#include + +#include +#include + +#ifdef BOOST_MYSQL_VALGRIND_TESTS +#include +#endif + +namespace boost { +namespace mysql { +namespace detail { + +// Valgrind +#ifdef BOOST_MYSQL_VALGRIND_TESTS +inline void valgrind_make_mem_defined(const void* data, std::size_t size) +{ + VALGRIND_MAKE_MEM_DEFINED(data, size); +} +#else +inline void valgrind_make_mem_defined(const void*, std::size_t) noexcept {} +#endif + +class algo_runner : asio::coroutine +{ + any_algo_ref algo_; + +public: + algo_runner(any_algo_ref algo) : algo_(algo) {} + + connection_state_data& conn_state() noexcept { return algo_.get().conn_state(); } + const connection_state_data& conn_state() const noexcept { return algo_.get().conn_state(); } + + asio::const_buffer write_buffer() const noexcept + { + return asio::buffer(conn_state().writer.current_chunk()); + } + + asio::mutable_buffer read_buffer() noexcept + { + auto buff = conn_state().reader.buffer(); + return asio::mutable_buffer(buff.data(), buff.size()); + } + + next_action resume(error_code ec, std::size_t bytes_transferred) + { + next_action act; + + BOOST_ASIO_CORO_REENTER(*this) + { + // Run until completion + while (true) + { + // Run the op + act = algo_.resume(ec); + + // Check next action + if (act.is_done()) + { + return act; + } + else if (act.type() == next_action::type_t::read) + { + // Read until a complete message is received + // (may be zero times if cached) + while (!conn_state().reader.done() && !ec) + { + conn_state().reader.prepare_buffer(); + BOOST_ASIO_CORO_YIELD return next_action::type_t::read; + valgrind_make_mem_defined(conn_state().reader.buffer().data(), bytes_transferred); + conn_state().reader.resume(bytes_transferred); + } + + // Check for errors + if (!ec) + ec = conn_state().reader.error(); + + // We've got a message, continue + } + else if (act.type() == next_action::type_t::write) + { + // Write until a complete message was written + while (!conn_state().writer.done() && !ec) + { + BOOST_ASIO_CORO_YIELD return next_action::type_t::write; + conn_state().writer.resume(bytes_transferred); + } + + // We fully wrote a message, continue + } + else + { + // Other ops always require I/O + BOOST_ASIO_CORO_YIELD return act; + } + } + } + + return next_action(); + } +}; + +} // namespace detail +} // namespace mysql +} // namespace boost + +#endif diff --git a/include/boost/mysql/impl/internal/sansio/connect.hpp b/include/boost/mysql/impl/internal/sansio/connect.hpp new file mode 100644 index 000000000..d91704510 --- /dev/null +++ b/include/boost/mysql/impl/internal/sansio/connect.hpp @@ -0,0 +1,80 @@ +// +// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) +// + +#ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_CONNECT_HPP +#define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_CONNECT_HPP + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +namespace boost { +namespace mysql { +namespace detail { + +class connect_algo : public sansio_algorithm, asio::coroutine +{ + const void* connect_arg_; + handshake_algo handshake_; + error_code stored_ec_; + +public: + connect_algo(connection_state_data& st, connect_op_params params) noexcept + : sansio_algorithm(st), + connect_arg_(params.connect_arg), + handshake_(st, {params.diag, params.hparams, params.transport_supports_ssl}) + { + } + + next_action resume(error_code ec) + { + next_action act; + + BOOST_ASIO_CORO_REENTER(*this) + { + // Clear diagnostics + handshake_.diag().clear(); + + // Physical connect + BOOST_ASIO_CORO_YIELD return connect(connect_arg_); + if (ec) + return ec; + + // Handshake + while (!(act = handshake_.resume(ec)).is_done()) + BOOST_ASIO_CORO_YIELD return act; + + // If handshake failed, close the stream ignoring the result + // and return handshake's error code + if (act.error()) + { + stored_ec_ = act.error(); + BOOST_ASIO_CORO_YIELD return close(); + return stored_ec_; + } + + // Done + } + + return next_action(); + } +}; + +} // namespace detail +} // namespace mysql +} // namespace boost + +#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP_ */ diff --git a/include/boost/mysql/impl/internal/sansio/connection_state.hpp b/include/boost/mysql/impl/internal/sansio/connection_state.hpp index 71ef3eaa9..d61fb59b8 100644 --- a/include/boost/mysql/impl/internal/sansio/connection_state.hpp +++ b/include/boost/mysql/impl/internal/sansio/connection_state.hpp @@ -15,7 +15,9 @@ #include +#include #include +#include #include #include #include @@ -26,10 +28,8 @@ #include #include #include -#include #include -#include #include #include @@ -40,15 +40,9 @@ namespace boost { namespace mysql { namespace detail { -class null_op_state -{ -public: - null_op_state() = default; - next_action resume(error_code ec) noexcept { return next_action(ec); } -}; - // clang-format off template struct op_state_type; +template <> struct op_state_type { using type = connect_algo; }; template <> struct op_state_type { using type = handshake_algo; }; template <> struct op_state_type { using type = execute_algo; }; template <> struct op_state_type { using type = start_execution_algo; }; @@ -65,17 +59,8 @@ template using op_state_type_t = typename op_state_type - any_algo_ref operator()(Algo& algo) const noexcept - { - return any_algo_ref(algo); - } - }; - using any_algo = variant2::variant< - null_op_state, + connect_algo, handshake_algo, execute_algo, start_execution_algo, @@ -89,30 +74,25 @@ class connection_state quit_connection_algo>; connection_state_data st_data_; - any_algo current_algo_; - asio::coroutine run_any_algo_coro_; - - any_algo_ref current_algo() { return variant2::visit(current_algo_visitor(), current_algo_); } + any_algo algo_; public: - connection_state(std::size_t read_buffer_size) : st_data_(read_buffer_size) {} + // We initialize the algo state with a dummy value. This will be overwritten + // by setup() before the first algorithm starts running. Doing this avoids + // the need for a special null algo + connection_state(std::size_t read_buffer_size) + : st_data_(read_buffer_size), algo_(ping_algo(st_data_, {&st_data_.shared_diag})) + { + } const connection_state_data& data() const noexcept { return st_data_; } connection_state_data& data() noexcept { return st_data_; } template - void setup(OpParams params) - { - current_algo_.emplace>(st_data_, params); - run_any_algo_coro_ = asio::coroutine(); - } - - asio::mutable_buffer read_buffer() noexcept + any_algo_ref setup(OpParams params) { - auto res = st_data_.reader.buffer(); - return asio::mutable_buffer(res.data(), res.size()); + return algo_.emplace>(st_data_, params); } - asio::const_buffer write_buffer() const noexcept { return asio::buffer(st_data_.writer.current_chunk()); } template void result(typename std::enable_if()>::type* = nullptr) const noexcept @@ -124,12 +104,7 @@ class connection_state typename std::enable_if()>::type* = nullptr ) const { - return variant2::get>(current_algo_).result(); - } - - next_action resume(error_code ec, std::size_t bytes_transferred) - { - return run_any_algo(run_any_algo_coro_, st_data_, current_algo(), ec, bytes_transferred); + return variant2::get>(algo_).result(); } }; diff --git a/include/boost/mysql/impl/internal/sansio/handshake.hpp b/include/boost/mysql/impl/internal/sansio/handshake.hpp index 2d2a316b2..f86635f40 100644 --- a/include/boost/mysql/impl/internal/sansio/handshake.hpp +++ b/include/boost/mysql/impl/internal/sansio/handshake.hpp @@ -180,6 +180,8 @@ class handshake_algo : public sansio_algorithm, asio::coroutine { } + diagnostics& diag() noexcept { return *diag_; } + next_action resume(error_code ec) { if (ec) diff --git a/include/boost/mysql/impl/internal/sansio/run_any_algo.hpp b/include/boost/mysql/impl/internal/sansio/run_any_algo.hpp deleted file mode 100644 index 15578fdaf..000000000 --- a/include/boost/mysql/impl/internal/sansio/run_any_algo.hpp +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) -// - -#ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_RUN_ANY_ALGO_HPP -#define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_RUN_ANY_ALGO_HPP - -#include - -#include -#include -#include - -#include - -#include - -namespace boost { -namespace mysql { -namespace detail { - -class any_algo_ref -{ - template - static next_action do_resume(void* self, error_code ec) - { - return static_cast(self)->resume(ec); - } - - using fn_t = next_action (*)(void*, error_code); - - void* op_{}; - fn_t fn_{}; - -public: - template < - class OpState, - class = typename std::enable_if::value>::type> - any_algo_ref(OpState& op) noexcept : op_(&op), fn_(&do_resume) - { - } - - next_action resume(error_code ec) { return fn_(op_, ec); } -}; - -inline next_action run_any_algo( - asio::coroutine& coro, - connection_state_data& st, - any_algo_ref op, - error_code ec, - std::size_t bytes_transferred -) -{ - next_action act; - - BOOST_ASIO_CORO_REENTER(coro) - { - // Run until completion - while (true) - { - // Run the op - act = op.resume(ec); - - // Check next action - if (act.is_done()) - { - return act; - } - else if (act.type() == next_action::type_t::read) - { - // Read until a complete message is received - // (may be zero times if cached) - while (!st.reader.done() && !ec) - { - st.reader.prepare_buffer(); - BOOST_ASIO_CORO_YIELD return next_action::type_t::read; - st.reader.resume(bytes_transferred); - } - - // Check for errors - if (!ec) - ec = st.reader.error(); - - // We've got a message, continue - } - else if (act.type() == next_action::type_t::write) - { - // Write until a complete message was written - while (!st.writer.done() && !ec) - { - BOOST_ASIO_CORO_YIELD return next_action::type_t::write; - st.writer.resume(bytes_transferred); - } - - // We fully wrote a message, continue - } - else - { - // Other ops always require I/O - BOOST_ASIO_CORO_YIELD return act; - } - } - } - - return next_action(); -} - -} // namespace detail -} // namespace mysql -} // namespace boost - -#endif diff --git a/include/boost/mysql/impl/internal/sansio/sansio_algorithm.hpp b/include/boost/mysql/impl/internal/sansio/sansio_algorithm.hpp index 3997c1ef4..54c5d0f7f 100644 --- a/include/boost/mysql/impl/internal/sansio/sansio_algorithm.hpp +++ b/include/boost/mysql/impl/internal/sansio/sansio_algorithm.hpp @@ -13,6 +13,8 @@ #include #include +#include + namespace boost { namespace mysql { namespace detail { @@ -37,9 +39,40 @@ class sansio_algorithm next_action ssl_handshake() const noexcept { return next_action(next_action::type_t::ssl_handshake); } next_action ssl_shutdown() const noexcept { return next_action(next_action::type_t::ssl_shutdown); } + next_action connect(const void* connect_arg) const noexcept { return next_action(connect_arg); } next_action close() const noexcept { return next_action(next_action::type_t::close); } sansio_algorithm(connection_state_data& st) noexcept : st_(&st) {} + +public: + const connection_state_data& conn_state() const noexcept { return *st_; } + connection_state_data& conn_state() noexcept { return *st_; } +}; + +class any_algo_ref +{ + template + static next_action do_resume(sansio_algorithm* self, error_code ec) + { + return static_cast(self)->resume(ec); + } + + using fn_t = next_action (*)(sansio_algorithm*, error_code); + + sansio_algorithm* algo_{}; + fn_t fn_{}; + +public: + template < + class Algo, + class = typename std::enable_if::value>::type> + any_algo_ref(Algo& op) noexcept : algo_(&op), fn_(&do_resume) + { + } + + sansio_algorithm& get() noexcept { return *algo_; } + const sansio_algorithm& get() const noexcept { return *algo_; } + next_action resume(error_code ec) { return fn_(algo_, ec); } }; } // namespace detail diff --git a/include/boost/mysql/impl/run_algo.ipp b/include/boost/mysql/impl/run_algo.ipp new file mode 100644 index 000000000..2fb8a4561 --- /dev/null +++ b/include/boost/mysql/impl/run_algo.ipp @@ -0,0 +1,175 @@ +// +// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail 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) +// + +#ifndef BOOST_MYSQL_IMPL_RUN_ALGO_IPP +#define BOOST_MYSQL_IMPL_RUN_ALGO_IPP + +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace boost { +namespace mysql { +namespace detail { + +template +class generic_algo_handler +{ +public: + static_assert(!has_void_result(), "AlgoParams::result_type should be non-void"); + + using result_t = typename AlgoParams::result_type; + + using final_handler_t = asio::any_completion_handler; + generic_algo_handler(final_handler_t&& h, connection_state& st) : final_handler_(std::move(h)), st_(&st) + { + } + + using allocator_type = final_handler_t::allocator_type; + using cancellation_slot_type = final_handler_t::cancellation_slot_type; + + allocator_type get_allocator() const noexcept { return final_handler_.get_allocator(); } + cancellation_slot_type get_cancellation_slot() const noexcept + { + return final_handler_.get_cancellation_slot(); + } + + const auto& handler() const noexcept { return final_handler_; } + + void operator()(error_code ec) + { + std::move(final_handler_)(ec, ec ? result_t{} : st_->result()); + } + +private: + final_handler_t final_handler_; + connection_state* st_; +}; + +template +asio::any_completion_handler make_handler( + completion_handler_t&& final_handler, + connection_state& st, + typename std::enable_if()>::type* = nullptr +) +{ + return generic_algo_handler(std::move(final_handler), st); +} + +template +asio::any_completion_handler make_handler( + asio::any_completion_handler&& final_handler, + connection_state&, + typename std::enable_if()>::type* = nullptr +) +{ + return std::move(final_handler); +} + +} // namespace detail +} // namespace mysql +} // namespace boost + +namespace boost { +namespace asio { + +template +struct associated_executor, Candidate> +{ + using type = any_completion_executor; + + static type get( + const mysql::detail::generic_algo_handler& handler, + const Candidate& candidate = Candidate() + ) noexcept + { + return asio::get_associated_executor(handler.handler(), candidate); + } +}; + +template +struct associated_immediate_executor, Candidate> +{ + using type = any_completion_executor; + + static type get( + const mysql::detail::generic_algo_handler& handler, + const Candidate& candidate = Candidate() + ) BOOST_ASIO_NOEXCEPT + { + return asio::get_associated_immediate_executor(handler.handler(), candidate); + } +}; + +} // namespace asio +} // namespace boost + +template +typename OpParams::result_type boost::mysql::detail::run_algo( + any_stream& stream, + connection_state& st, + OpParams params, + error_code& ec +) +{ + auto algo = st.setup(params); + run_algo_impl(stream, algo, ec); + return st.result(); +} + +template +void boost::mysql::detail::async_run_algo( + any_stream& stream, + connection_state& st, + OpParams params, + completion_handler_t final_handler +) +{ + auto handler = make_handler(std::move(final_handler), st); + auto algo = st.setup(params); + async_run_algo_impl(stream, algo, std::move(handler)); +} + +#ifdef BOOST_MYSQL_SEPARATE_COMPILATION + +#define BOOST_MYSQL_INSTANTIATE_ALGO(op_params_type) \ + template op_params_type::result_type \ + run_algo(any_stream&, connection_state&, op_params_type, error_code&); \ + template void async_run_algo< \ + op_params_type>(any_stream&, connection_state&, op_params_type, completion_handler_t); + +namespace boost { +namespace mysql { +namespace detail { + +BOOST_MYSQL_INSTANTIATE_ALGO(connect_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(handshake_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(execute_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(start_execution_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(read_resultset_head_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(read_some_rows_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(read_some_rows_dynamic_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(prepare_statement_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(close_statement_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(ping_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(reset_connection_op_params) +BOOST_MYSQL_INSTANTIATE_ALGO(quit_connection_op_params) + +} // namespace detail +} // namespace mysql +} // namespace boost + +#endif + +#endif diff --git a/include/boost/mysql/src.hpp b/include/boost/mysql/src.hpp index 010bd8a1b..c9c239d8f 100644 --- a/include/boost/mysql/src.hpp +++ b/include/boost/mysql/src.hpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -40,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 61264aaa0..188f6bc2e 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -28,7 +28,7 @@ add_executable( test/sansio/read_buffer.cpp test/sansio/message_writer.cpp test/sansio/message_reader.cpp - test/sansio/run_any_algo.cpp + test/sansio/algo_runner.cpp # test/channel/message_parser.cpp # test/channel/write_message.cpp diff --git a/test/unit/include/test_unit/algo_test.hpp b/test/unit/include/test_unit/algo_test.hpp index d6344d9f9..58866a0ec 100644 --- a/test/unit/include/test_unit/algo_test.hpp +++ b/test/unit/include/test_unit/algo_test.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -75,11 +75,7 @@ class BOOST_ATTRIBUTE_NODISCARD algo_test return *this; } - detail::next_action run_algo_until_step( - detail::connection_state_data& st, - detail::any_algo_ref algo, - std::size_t num_steps_to_run - ) + detail::next_action run_algo_until_step(detail::any_algo_ref algo, std::size_t num_steps_to_run) { assert(num_steps_to_run <= num_steps()); @@ -94,9 +90,9 @@ class BOOST_ATTRIBUTE_NODISCARD algo_test const auto& step = steps_[i]; BOOST_TEST_REQUIRE(act.type() == step.type); if (step.type == detail::next_action::type_t::read) - handle_read(step, st); + handle_read(step, algo.get().conn_state()); else if (step.type == detail::next_action::type_t::write) - handle_write(step, st); + handle_write(step, algo.get().conn_state()); // Other actions don't need any handling act = algo.resume(step.result); @@ -106,6 +102,34 @@ class BOOST_ATTRIBUTE_NODISCARD algo_test return act; } + std::size_t num_steps() const noexcept { return steps_.size(); } + + void check_impl(detail::any_algo_ref algo, error_code expected_ec = {}) + { + // Run the op until completion + auto act = run_algo_until_step(algo, steps_.size()); + + // Check that we've finished + BOOST_TEST_REQUIRE(act.type() == detail::next_action::type_t::none); + BOOST_TEST(act.error() == expected_ec); + } + + void check_network_errors_impl(detail::any_algo_ref algo, std::size_t step_number) + { + assert(step_number < num_steps()); + + // Run all the steps that shouldn't cause an error + auto act = run_algo_until_step(algo, step_number); + BOOST_TEST_REQUIRE(act.type() == steps_[step_number].type); + + // Trigger an error in the requested step + act = algo.resume(asio::error::bad_descriptor); + + // The operation finished and returned the network error + BOOST_TEST_REQUIRE(act.type() == detail::next_action::type_t::none); + BOOST_TEST(act.error() == error_code(asio::error::bad_descriptor)); + } + public: algo_test() = default; @@ -145,45 +169,13 @@ class BOOST_ATTRIBUTE_NODISCARD algo_test return add_step(detail::next_action::type_t::close, {}, result); } - std::size_t num_steps() const noexcept { return steps_.size(); } - - void check(detail::connection_state_data& st, detail::any_algo_ref algo, error_code expected_ec = {}) - { - // Run the op until completion - auto act = run_algo_until_step(st, algo, steps_.size()); - - // Check that we've finished - BOOST_TEST_REQUIRE(act.type() == detail::next_action::type_t::none); - BOOST_TEST(act.error() == expected_ec); - } - template void check(AlgoFixture& fix, error_code expected_ec = {}, const diagnostics& expected_diag = {}) { - check(fix.st, fix.algo, expected_ec); + check_impl(fix.algo, expected_ec); BOOST_TEST(fix.diag == expected_diag); } - void check_network_errors( - detail::connection_state_data& st, - detail::any_algo_ref algo, - std::size_t step_number - ) - { - assert(step_number < num_steps()); - - // Run all the steps that shouldn't cause an error - auto act = run_algo_until_step(st, algo, step_number); - BOOST_TEST_REQUIRE(act.type() == steps_[step_number].type); - - // Trigger an error in the requested step - act = algo.resume(asio::error::bad_descriptor); - - // The operation finished and returned the network error - BOOST_TEST_REQUIRE(act.type() == detail::next_action::type_t::none); - BOOST_TEST(act.error() == error_code(asio::error::bad_descriptor)); - } - template void check_network_errors() { @@ -192,7 +184,7 @@ class BOOST_ATTRIBUTE_NODISCARD algo_test BOOST_TEST_CONTEXT("check_network_errors erroring at step " << i) { AlgoFixture fix; - check_network_errors(fix.st, fix.algo, i); + check_network_errors_impl(fix.algo, i); BOOST_TEST(fix.diag == diagnostics()); } } diff --git a/test/unit/test/sansio/run_any_algo.cpp b/test/unit/test/sansio/algo_runner.cpp similarity index 87% rename from test/unit/test/sansio/run_any_algo.cpp rename to test/unit/test/sansio/algo_runner.cpp index 86c44652d..0ddc8b4d6 100644 --- a/test/unit/test/sansio/run_any_algo.cpp +++ b/test/unit/test/sansio/algo_runner.cpp @@ -8,10 +8,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -79,17 +79,17 @@ BOOST_AUTO_TEST_CASE(read_cached) }; connection_state_data st(512); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a read request. We don't have cached data, so run_op returns it - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::read); // Acknowledge the read request auto bytes = concat_copy(create_frame(0, msg1), create_frame(1, msg2)); transfer(st.reader, bytes); - act = run_any_algo(coro, st, algo, error_code(), bytes.size()); + act = runner.resume(error_code(), bytes.size()); // The second read request is acknowledged directly, since it has cached data BOOST_TEST(act.success()); @@ -119,29 +119,29 @@ BOOST_AUTO_TEST_CASE(read_short_and_buffer_resizing) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a read request and resizes the buffer aprorpiately - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::read); // Acknowledge the read request. There is space for the header, at least auto bytes = create_frame(0, msg2); transfer(st.reader, span(bytes.data(), 4)); - act = run_any_algo(coro, st, algo, error_code(), 4); + act = runner.resume(error_code(), 4); // The read request wasn't completely satisified, so more bytes are asked for BOOST_TEST(act.type() == next_action::type_t::read); // Read part of the body transfer(st.reader, span(bytes.data() + 4, 10)); - act = run_any_algo(coro, st, algo, error_code(), 10); + act = runner.resume(error_code(), 10); BOOST_TEST(act.type() == next_action::type_t::read); // Complete transfer(st.reader, span(bytes.data() + 14, bytes.size() - 14)); - act = run_any_algo(coro, st, algo, error_code(), bytes.size() - 14); + act = runner.resume(error_code(), bytes.size() - 14); BOOST_TEST(act.success()); } @@ -167,17 +167,17 @@ BOOST_AUTO_TEST_CASE(read_parsing_error) }; connection_state_data st(512); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a read request. We don't have cached data, so run_op returns it - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::read); // Acknowledge the read request. This causes a seqnum mismatch that is transmitted to the op auto bytes = create_frame(0, msg1); transfer(st.reader, bytes); - act = run_any_algo(coro, st, algo, error_code(), bytes.size()); + act = runner.resume(error_code(), bytes.size()); // Op done BOOST_TEST(act.success()); @@ -205,15 +205,15 @@ BOOST_AUTO_TEST_CASE(read_io_error) }; connection_state_data st(512); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a read request. We don't have cached data, so run_op returns it - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::read); // Read request fails with an error - act = run_any_algo(coro, st, algo, client_errc::wrong_num_params, 0); + act = runner.resume(client_errc::wrong_num_params, 0); // Op done BOOST_TEST(act.success()); @@ -254,21 +254,21 @@ BOOST_AUTO_TEST_CASE(write_short) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a write request - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::write); BOOST_MYSQL_ASSERT_BUFFER_EQUALS(st.writer.current_chunk(), create_frame(0, msg1)); // Acknowledge part of the write. This will ask for more bytes to be written - act = run_any_algo(coro, st, algo, error_code(), 4); + act = runner.resume(error_code(), 4); BOOST_TEST(act.type() == next_action::type_t::write); BOOST_MYSQL_ASSERT_BUFFER_EQUALS(st.writer.current_chunk(), msg1); // Complete - act = run_any_algo(coro, st, algo, error_code(), 3); + act = runner.resume(error_code(), 3); BOOST_TEST(act.success()); } @@ -294,13 +294,13 @@ BOOST_AUTO_TEST_CASE(write_io_error) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a write request. Fail it - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::write); - act = run_any_algo(coro, st, algo, client_errc::wrong_num_params, 0); + act = runner.resume(client_errc::wrong_num_params, 0); // Done BOOST_TEST(act.success()); @@ -327,15 +327,15 @@ BOOST_AUTO_TEST_CASE(ssl_handshake) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a SSL handshake request. These are always returned - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::ssl_handshake); // Fail the op - act = run_any_algo(coro, st, algo, client_errc::wrong_num_params, 0); + act = runner.resume(client_errc::wrong_num_params, 0); // Done BOOST_TEST(act.success()); @@ -362,15 +362,15 @@ BOOST_AUTO_TEST_CASE(ssl_shutdown) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields a SSL handshake request. These are always returned - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.type() == next_action::type_t::ssl_shutdown); // Fail the op - act = run_any_algo(coro, st, algo, client_errc::wrong_num_params, 0); + act = runner.resume(client_errc::wrong_num_params, 0); // Done BOOST_TEST(act.success()); @@ -397,11 +397,11 @@ BOOST_AUTO_TEST_CASE(immediate_completion) }; connection_state_data st(0); - coroutine coro; mock_algo algo(st); + algo_runner runner(algo); // Initial run yields completion - auto act = run_any_algo(coro, st, algo, error_code(), 0); + auto act = runner.resume(error_code(), 0); BOOST_TEST(act.success()); }