Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Immediate executor #2672

Merged
merged 1 commit into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions include/boost/beast/_experimental/http/icy_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
71 changes: 71 additions & 0 deletions include/boost/beast/_experimental/test/immediate_executor.hpp
Original file line number Diff line number Diff line change
@@ -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 <boost/asio/any_io_executor.hpp>

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<asio::execution_context*>(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<class F>
void
execute(F f) const
{
count_++;
std::forward<F>(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
25 changes: 14 additions & 11 deletions include/boost/beast/_experimental/test/stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
71 changes: 54 additions & 17 deletions include/boost/beast/core/async_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
#include <boost/beast/core/detail/work_guard.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/associated_immediate_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
ashtum marked this conversation as resolved.
Show resolved Hide resolved
#include <boost/core/exchange.hpp>
#include <boost/core/empty_value.hpp>
Expand All @@ -30,6 +32,7 @@
namespace boost {
namespace beast {


/** Base class to assist writing composed operations.

A function object submitted to intermediate initiating functions during
Expand Down Expand Up @@ -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<Executor1>::executor_type
>::type;
#endif


private:

virtual
void
Expand Down Expand Up @@ -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<net::associated_cancellation_slot_t<Handler>>;

Expand Down Expand Up @@ -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.

Expand All @@ -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>(args)...)));
auto const ex = this->get_immediate_executor();
net::dispatch(
ex,
beast::bind_front_handler(
std::move(h_),
std::forward<Args>(args)...));
wg1_.reset();
}
else
Expand Down
Loading