diff --git a/include/boost/beast/_experimental/http/icy_stream.hpp b/include/boost/beast/_experimental/http/icy_stream.hpp index 81730b5801..d033935136 100644 --- a/include/boost/beast/_experimental/http/icy_stream.hpp +++ b/include/boost/beast/_experimental/http/icy_stream.hpp @@ -212,11 +212,13 @@ class icy_stream std::size_t bytes_transferred // Number of bytes read. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_read_some` operation may not read all of the requested number of bytes. Consider using the function `net::async_read` if you need to ensure that the requested amount of data is read before the asynchronous @@ -295,11 +297,13 @@ class icy_stream std::size_t bytes_transferred // Number of bytes written. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_write_some` operation may not transmit all of the data to the peer. Consider using the function `net::async_write` if you need to ensure that all data is written before the asynchronous operation completes. diff --git a/include/boost/beast/_experimental/test/immediate_executor.hpp b/include/boost/beast/_experimental/test/immediate_executor.hpp new file mode 100644 index 0000000000..cb469fcbb7 --- /dev/null +++ b/include/boost/beast/_experimental/test/immediate_executor.hpp @@ -0,0 +1,71 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// 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_BEAST_TEST_IMMEDIATE_EXECUTOR_HPP +#define BOOST_BEAST_TEST_IMMEDIATE_EXECUTOR_HPP + +#include + +namespace boost +{ +namespace beast +{ +namespace test +{ + +/** A immediate executor that directly invokes and counts how often that happened. */ + +class immediate_executor +{ + std::size_t &count_; + public: + immediate_executor(std::size_t & count) noexcept : count_(count) {} + + asio::execution_context &query(asio::execution::context_t) const + { + BOOST_ASSERT(false); + return *static_cast(nullptr); + } + + constexpr static asio::execution::blocking_t + query(asio::execution::blocking_t) noexcept + { + return asio::execution::blocking_t::never_t{}; + } + + constexpr static asio::execution::relationship_t + query(asio::execution::relationship_t) noexcept + { + return asio::execution::relationship_t::fork_t{}; + } + // this function takes the function F and runs it on the event loop. + template + void + execute(F f) const + { + count_++; + std::forward(f)(); + } + + bool + operator==(immediate_executor const &other) const noexcept + { + return true; + } + + bool + operator!=(immediate_executor const &other) const noexcept + { + return false; + } +}; + +} +} +} + +#endif //BOOST_BEAST_TEST_IMMEDIATE_EXECUTOR_HPP diff --git a/include/boost/beast/_experimental/test/stream.hpp b/include/boost/beast/_experimental/test/stream.hpp index a3b0236150..8e87a9e504 100644 --- a/include/boost/beast/_experimental/test/stream.hpp +++ b/include/boost/beast/_experimental/test/stream.hpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -454,11 +453,13 @@ class basic_stream std::size_t bytes_transferred // Number of bytes read. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_read_some` operation may not read all of the requested number of bytes. Consider using the function `net::async_read` if you need to ensure that the requested amount of data is read before the asynchronous @@ -534,11 +535,13 @@ class basic_stream std::size_t bytes_transferred // Number of bytes written. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_write_some` operation may not transmit all of the data to the peer. Consider using the function `net::async_write` if you need to ensure that all data is written before the asynchronous operation completes. diff --git a/include/boost/beast/core/async_base.hpp b/include/boost/beast/core/async_base.hpp index 6dd7b178bc..cd185189af 100644 --- a/include/boost/beast/core/async_base.hpp +++ b/include/boost/beast/core/async_base.hpp @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -30,6 +32,7 @@ namespace boost { namespace beast { + /** Base class to assist writing composed operations. A function object submitted to intermediate initiating functions during @@ -207,7 +210,25 @@ class async_base >::type; #endif -private: + /** The type of the immediate executor associated with this object. + + If a class derived from @ref boost::beast::async_base is a completion + handler, then the associated immediage executor of the derived class will + be this type. +*/ + using immediate_executor_type = +#if BOOST_BEAST_DOXYGEN + __implementation_defined__; +#else + typename + net::associated_immediate_executor< + Handler, + typename detail::select_work_guard_t::executor_type + >::type; +#endif + + + private: virtual void @@ -309,15 +330,31 @@ class async_base h_, wg1_.get_executor()); } - /** The type of cancellation_slot associated with this object. - - If a class derived from @ref async_base is a completion - handler, then the associated cancellation_slot of the - derived class will be this type. + /** Returns the immediate executor associated with this handler. + If the handler has none it returns asios default immediate + executor based on the executor of the object. - The default type is a filtering cancellation slot, - that only allows terminal cancellation. + If a class derived from @ref boost::beast::async_base is a completion + handler, then the object returned from this function will be used + as the associated immediate executor of the derived class. */ + immediate_executor_type + get_immediate_executor() const noexcept + { + return net::get_associated_immediate_executor( + h_, wg1_.get_executor()); + } + + + /** The type of cancellation_slot associated with this object. + + If a class derived from @ref async_base is a completion + handler, then the associated cancellation_slot of the + derived class will be this type. + + The default type is a filtering cancellation slot, + that only allows terminal cancellation. + */ using cancellation_slot_type = beast::detail::filtering_cancellation_slot>; @@ -373,7 +410,9 @@ class async_base is invoked. @param is_continuation If this value is `false`, then the - handler will be submitted to the executor using `net::post`. + handler will be submitted to the to the immediate executor using + `net::dispatch`. If the handler has no immediate executor, + this will submit to the executor via `net::post`. Otherwise the handler will be invoked as if by calling @ref boost::beast::async_base::complete_now. @@ -388,14 +427,12 @@ class async_base this->before_invoke_hook(); if(! is_continuation) { - auto const ex = get_executor(); - net::post( - wg1_.get_executor(), - net::bind_executor( - ex, - beast::bind_front_handler( - std::move(h_), - std::forward(args)...))); + auto const ex = this->get_immediate_executor(); + net::dispatch( + ex, + beast::bind_front_handler( + std::move(h_), + std::forward(args)...)); wg1_.reset(); } else diff --git a/include/boost/beast/core/basic_stream.hpp b/include/boost/beast/core/basic_stream.hpp index 2ba8f3a6a6..8b6ae39d41 100644 --- a/include/boost/beast/core/basic_stream.hpp +++ b/include/boost/beast/core/basic_stream.hpp @@ -928,10 +928,13 @@ class basic_stream error_code ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation @@ -993,10 +996,13 @@ class basic_stream typename Protocol::endpoint const& endpoint ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation @@ -1079,11 +1085,13 @@ class basic_stream typename Protocol::endpoint const& endpoint ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Example The following connect condition function object can be used to output information about the individual connection attempts: @@ -1173,11 +1181,13 @@ class basic_stream Iterator iterator ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -1244,11 +1254,13 @@ class basic_stream Iterator iterator ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -1377,11 +1389,13 @@ class basic_stream std::size_t bytes_transferred // Number of bytes read. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_read_some` operation may not receive all of the requested number of bytes. Consider using the function `net::async_read` if you need to ensure that the requested amount of data is read before the asynchronous @@ -1511,11 +1525,13 @@ class basic_stream std::size_t bytes_transferred // Number of bytes written. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_write_some` operation may not transmit all of the requested number of bytes. Consider using the function `net::async_write` if you need to ensure that the requested amount of data is sent before the asynchronous diff --git a/include/boost/beast/core/buffered_read_stream.hpp b/include/boost/beast/core/buffered_read_stream.hpp index 2d86a63180..2b25da4903 100644 --- a/include/boost/beast/core/buffered_read_stream.hpp +++ b/include/boost/beast/core/buffered_read_stream.hpp @@ -256,11 +256,13 @@ class buffered_read_stream std::size_t bytes_transferred // number of bytes transferred ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - */ + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. */ template< class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler = @@ -335,11 +337,13 @@ class buffered_read_stream std::size_t bytes_transferred // number of bytes transferred ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - */ + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. */ template< class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler = diff --git a/include/boost/beast/core/buffers_generator.hpp b/include/boost/beast/core/buffers_generator.hpp index 5394865e00..dfc1c2a512 100644 --- a/include/boost/beast/core/buffers_generator.hpp +++ b/include/boost/beast/core/buffers_generator.hpp @@ -167,10 +167,11 @@ write( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from - within this function. Invocation of the handler will be - performed in a manner equivalent to using `net::post`. + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. @see BuffersGenerator */ diff --git a/include/boost/beast/core/detail/read.hpp b/include/boost/beast/core/detail/read.hpp index 91a02b8321..fac87aa07b 100644 --- a/include/boost/beast/core/detail/read.hpp +++ b/include/boost/beast/core/detail/read.hpp @@ -210,8 +210,9 @@ read( // prior to the error. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. */ diff --git a/include/boost/beast/core/detect_ssl.hpp b/include/boost/beast/core/detect_ssl.hpp index ea6c56f9c4..f497f7b42d 100644 --- a/include/boost/beast/core/detect_ssl.hpp +++ b/include/boost/beast/core/detect_ssl.hpp @@ -308,8 +308,9 @@ detect_ssl( bool result // The result of the detector ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. */ diff --git a/include/boost/beast/core/flat_stream.hpp b/include/boost/beast/core/flat_stream.hpp index 913fdc3e10..42844ba5f2 100644 --- a/include/boost/beast/core/flat_stream.hpp +++ b/include/boost/beast/core/flat_stream.hpp @@ -241,11 +241,13 @@ class flat_stream std::size_t bytes_transferred // Number of bytes read. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `read_some` operation may not read all of the requested number of bytes. Consider using the function `net::async_read` if you need to ensure that the requested amount of data is read before the asynchronous @@ -323,11 +325,13 @@ class flat_stream std::size_t bytes_transferred // Number of bytes written. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @note The `async_write_some` operation may not transmit all of the data to the peer. Consider using the function `net::async_write` if you need to ensure that all data is written before the asynchronous operation completes. diff --git a/include/boost/beast/core/impl/buffered_read_stream.hpp b/include/boost/beast/core/impl/buffered_read_stream.hpp index 097deaceaf..6b861c89f7 100644 --- a/include/boost/beast/core/impl/buffered_read_stream.hpp +++ b/include/boost/beast/core/impl/buffered_read_stream.hpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include namespace boost { @@ -80,11 +80,13 @@ class read_op std::move(*this)); } step_ = 3; - return net::post( - s_.get_executor(), - beast::bind_front_handler( - std::move(*this), ec, 0)); - + { + const auto ex = this->get_immediate_executor(); + return net::dispatch( + ex, + beast::bind_front_handler( + std::move(*this), ec, 0)); + } case 1: // upcall break; diff --git a/include/boost/beast/http/impl/read.hpp b/include/boost/beast/http/impl/read.hpp index e320014e9b..09c8303d0f 100644 --- a/include/boost/beast/http/impl/read.hpp +++ b/include/boost/beast/http/impl/read.hpp @@ -244,8 +244,13 @@ class read_some_op : asio::coroutine __FILE__, __LINE__, "http::async_read_some")); - net::post( - s_.get_executor(), + + const auto ex = + asio::get_associated_immediate_executor( + self, s_.get_executor()); + + net::dispatch( + ex, beast::bind_front_handler(std::move(self), ec)); } } @@ -285,7 +290,11 @@ class read_op __FILE__, __LINE__, "http::async_read")); - net::post(s_.get_executor(), std::move(self)); + const auto ex = + asio::get_associated_immediate_executor( + self, s_.get_executor()); + + net::dispatch(ex, std::move(self)); } } else diff --git a/include/boost/beast/http/impl/write.hpp b/include/boost/beast/http/impl/write.hpp index 1739e0243b..f76ea68c4b 100644 --- a/include/boost/beast/http/impl/write.hpp +++ b/include/boost/beast/http/impl/write.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -102,8 +102,9 @@ class write_some_op __FILE__, __LINE__, "http::async_write_some")); - return net::post( - s_.get_executor(), + auto ex = asio::get_associated_immediate_executor(*this, s_.get_executor()); + return net::dispatch( + ex, beast::bind_front_handler( std::move(*this), ec, 0)); } @@ -120,8 +121,9 @@ class write_some_op __FILE__, __LINE__, "http::async_write_some")); - return net::post( - s_.get_executor(), + const auto ex = this->get_immediate_executor(); + return net::dispatch( + ex, beast::bind_front_handler( std::move(*this), ec, 0)); } @@ -219,8 +221,9 @@ class write_op __FILE__, __LINE__, "http::async_write")); - net::post( - s_.get_executor(), + const auto ex = this->get_immediate_executor(); + net::dispatch( + ex, std::move(*this)); } goto upcall; diff --git a/include/boost/beast/http/read.hpp b/include/boost/beast/http/read.hpp index 1d0d8e9ff9..ffe0793cbf 100644 --- a/include/boost/beast/http/read.hpp +++ b/include/boost/beast/http/read.hpp @@ -192,8 +192,9 @@ read_some( std::size_t bytes_transferred // the total number of bytes transferred from the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -400,8 +401,9 @@ read_header( std::size_t bytes_transferred // the total number of bytes transferred from the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -610,8 +612,9 @@ read( std::size_t bytes_transferred // the total number of bytes transferred from the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -829,8 +832,9 @@ read( std::size_t bytes_transferred // the total number of bytes transferred from the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. diff --git a/include/boost/beast/http/write.hpp b/include/boost/beast/http/write.hpp index 50022e37a9..92050d27b4 100644 --- a/include/boost/beast/http/write.hpp +++ b/include/boost/beast/http/write.hpp @@ -156,8 +156,9 @@ write_some( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -295,8 +296,9 @@ write_header( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -431,8 +433,9 @@ write( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -668,8 +671,9 @@ write( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -742,8 +746,9 @@ async_write( std::size_t bytes_transferred // the number of bytes written to the stream ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. diff --git a/include/boost/beast/websocket/impl/accept.hpp b/include/boost/beast/websocket/impl/accept.hpp index 4cd9bb84f3..6cc4c88b18 100644 --- a/include/boost/beast/websocket/impl/accept.hpp +++ b/include/boost/beast/websocket/impl/accept.hpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/beast/websocket/impl/close.hpp b/include/boost/beast/websocket/impl/close.hpp index 8c823542ae..85882186de 100644 --- a/include/boost/beast/websocket/impl/close.hpp +++ b/include/boost/beast/websocket/impl/close.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include @@ -106,7 +106,8 @@ class stream::close_op __FILE__, __LINE__, "websocket::async_close")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.wr_block.is_locked(this)); } @@ -167,7 +168,8 @@ class stream::close_op __FILE__, __LINE__, "websocket::async_close")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.rd_block.is_locked(this)); if(impl.check_stop_now(ec)) diff --git a/include/boost/beast/websocket/impl/ping.hpp b/include/boost/beast/websocket/impl/ping.hpp index 2cbd2f7124..67d6cd3bbc 100644 --- a/include/boost/beast/websocket/impl/ping.hpp +++ b/include/boost/beast/websocket/impl/ping.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -99,7 +100,8 @@ class stream::ping_op __FILE__, __LINE__, "websocket::async_ping")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.wr_block.is_locked(this)); } diff --git a/include/boost/beast/websocket/impl/read.hpp b/include/boost/beast/websocket/impl/read.hpp index c3338a7357..e34a09bc0a 100644 --- a/include/boost/beast/websocket/impl/read.hpp +++ b/include/boost/beast/websocket/impl/read.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -122,7 +121,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.rd_block.is_locked(this)); @@ -238,7 +238,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(cont); // VFALCO call check_stop_now() here? @@ -291,7 +292,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.wr_block.is_locked(this)); if(impl.check_stop_now(ec)) @@ -335,7 +337,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(cont); } @@ -366,7 +369,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(cont); } @@ -648,7 +652,8 @@ class stream::read_some_op __FILE__, __LINE__, "websocket::async_read_some")); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.wr_block.is_locked(this)); if(impl.check_stop_now(ec)) diff --git a/include/boost/beast/websocket/impl/teardown.hpp b/include/boost/beast/websocket/impl/teardown.hpp index 43e63c2db2..f04be03b6f 100644 --- a/include/boost/beast/websocket/impl/teardown.hpp +++ b/include/boost/beast/websocket/impl/teardown.hpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include namespace boost { @@ -128,7 +128,8 @@ class teardown_tcp_op "websocket::tcp::async_teardown" )); - net::post(s_.get_executor(), bind_front_handler( + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, bind_front_handler( std::move(*this), ec)); } } diff --git a/include/boost/beast/websocket/impl/write.hpp b/include/boost/beast/websocket/impl/write.hpp index 69f2927e6e..a70cb1a091 100644 --- a/include/boost/beast/websocket/impl/write.hpp +++ b/include/boost/beast/websocket/impl/write.hpp @@ -199,7 +199,8 @@ operator()( "websocket::async_write_some" )); - net::post(sp->stream().get_executor(), std::move(*this)); + const auto ex = this->get_immediate_executor(); + net::dispatch(ex, std::move(*this)); } BOOST_ASSERT(impl.wr_block.is_locked(this)); } diff --git a/include/boost/beast/websocket/ssl.hpp b/include/boost/beast/websocket/ssl.hpp index 3fe63f41f2..7f2a9cabe9 100644 --- a/include/boost/beast/websocket/ssl.hpp +++ b/include/boost/beast/websocket/ssl.hpp @@ -63,8 +63,9 @@ teardown( error_code const& error // result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index 7c1f118b67..2f7fd6f5c4 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -931,11 +931,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Example @code ws.async_handshake("localhost", "/", @@ -1013,11 +1015,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Example @code response_type res; @@ -1354,11 +1358,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @see @li Websocket Opening Handshake Server Requirements (RFC6455) */ @@ -1421,11 +1427,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @see @li Websocket Opening Handshake Server Requirements (RFC6455) */ @@ -1486,11 +1494,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @see @li Websocket Opening Handshake Server Requirements (RFC6455) */ @@ -1622,11 +1632,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -1741,11 +1753,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -1864,11 +1878,13 @@ class stream error_code const& ec // Result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -2040,11 +2056,13 @@ class stream std::size_t bytes_written // Number of bytes appended to buffer ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -2234,11 +2252,13 @@ class stream std::size_t bytes_written // Number of bytes appended to buffer ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -2439,11 +2459,13 @@ class stream std::size_t bytes_written // Number of bytes written to the buffers ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation @@ -2578,11 +2600,13 @@ class stream // this will be less than the buffer_size. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following @@ -2717,11 +2741,13 @@ class stream // this will be less than the buffer_size. ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within + this function. Invocation of the handler will be performed + by dispatching to the immediate executor. If no + immediate executor is specified, this is equivalent + to using `net::post`. @par Per-Operation Cancellation This asynchronous operation supports cancellation for the following diff --git a/include/boost/beast/websocket/teardown.hpp b/include/boost/beast/websocket/teardown.hpp index 5ffbdcd8d6..ab5321e472 100644 --- a/include/boost/beast/websocket/teardown.hpp +++ b/include/boost/beast/websocket/teardown.hpp @@ -78,8 +78,9 @@ teardown( error_code const& error // result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. @@ -158,8 +159,9 @@ teardown( error_code const& error // result of operation ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within + If the handler has an associated immediate executor, + an immediate completion will be dispatched to it. + Otherwise, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using `net::post`. diff --git a/test/beast/websocket/close.cpp b/test/beast/websocket/close.cpp index a46e89c065..958ef4601d 100644 --- a/test/beast/websocket/close.cpp +++ b/test/beast/websocket/close.cpp @@ -12,10 +12,12 @@ #include #include +#include #include "test.hpp" #include #include +#include #if BOOST_ASIO_HAS_CO_AWAIT #include #endif @@ -729,6 +731,55 @@ class close_test : public websocket_test_suite ws.async_close({}, move_only_handler{}); } + void + testImmediate() + { + doFailLoop([&](test::fail_count& fc) + { + std::string const s = "Hello, world!"; + multi_buffer b; + net::io_context ioc; + stream ws{ioc, fc}; + std::size_t count = 0; + std::size_t ic = 0u; + test::immediate_executor imex{ic}; + + ws.async_read(b, + asio::bind_immediate_executor(imex, + [&](error_code ec, std::size_t) + { + if(ec != net::error::operation_aborted) + BOOST_THROW_EXCEPTION( + system_error{ec}); + ++count; + })); + + ws.async_write(net::buffer(s), + asio::bind_immediate_executor(imex, + [&](error_code ec, std::size_t) + { + if(ec != net::error::operation_aborted) + BOOST_THROW_EXCEPTION( + system_error{ec}); + ++count; + })); + ws.async_ping({}, + asio::bind_immediate_executor(imex, + [&](error_code ec) + { + if(ec != net::error::operation_aborted) + BOOST_THROW_EXCEPTION( + system_error{ec}); + ++count; + })); + + BEAST_EXPECT(ioc.run() == 0); + BEAST_EXPECT(count == 3); + BEAST_EXPECT(ic == 6); + }); + } + + struct copyable_handler { template @@ -754,6 +805,7 @@ class close_test : public websocket_test_suite testTimeout(); testSuspend(); testMoveOnly(); + testImmediate(); #if BOOST_ASIO_HAS_CO_AWAIT boost::ignore_unused(&close_test::testAwaitableCompiles); #endif