Initial Commit

This commit is contained in:
2025-11-06 15:51:56 +01:00
commit 0f7191ad54
648 changed files with 170981 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
//
// experimental/detail/channel_operation.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associated_allocator.hpp"
#include "asio/associated_executor.hpp"
#include "asio/associated_immediate_executor.hpp"
#include "asio/detail/initiate_post.hpp"
#include "asio/detail/initiate_dispatch.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution/outstanding_work.hpp"
#include "asio/executor_work_guard.hpp"
#include "asio/prefer.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
// Base class for all channel operations. A function pointer is used instead of
// virtual functions to avoid the associated overhead.
class channel_operation ASIO_INHERIT_TRACKED_HANDLER
{
public:
template <typename Executor, typename = void, typename = void>
class handler_work_base;
template <typename Handler, typename IoExecutor, typename = void>
class handler_work;
void destroy()
{
func_(this, destroy_op, 0);
}
protected:
enum action
{
destroy_op = 0,
immediate_op = 1,
post_op = 2,
dispatch_op = 3,
cancel_op = 4,
close_op = 5
};
typedef void (*func_type)(channel_operation*, action, void*);
channel_operation(func_type func)
: next_(0),
func_(func),
cancellation_key_(0)
{
}
// Prevents deletion through this type.
~channel_operation()
{
}
friend class asio::detail::op_queue_access;
channel_operation* next_;
func_type func_;
public:
// The operation key used for targeted cancellation.
void* cancellation_key_;
};
template <typename Executor, typename, typename>
class channel_operation::handler_work_base
{
public:
typedef decay_t<
prefer_result_t<Executor,
execution::outstanding_work_t::tracked_t
>
> executor_type;
handler_work_base(int, const Executor& ex)
: executor_(asio::prefer(ex, execution::outstanding_work.tracked))
{
}
const executor_type& get_executor() const noexcept
{
return executor_;
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor& io_exec, Function& function, Handler&)
{
(asio::detail::initiate_post_with_executor<IoExecutor>(io_exec))(
static_cast<Function&&>(function));
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(executor_,
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
private:
executor_type executor_;
};
template <typename Executor>
class channel_operation::handler_work_base<Executor,
enable_if_t<
execution::is_executor<Executor>::value
>,
enable_if_t<
can_require<Executor, execution::blocking_t::never_t>::value
>
>
{
public:
typedef decay_t<
prefer_result_t<Executor,
execution::outstanding_work_t::tracked_t
>
> executor_type;
handler_work_base(int, const Executor& ex)
: executor_(asio::prefer(ex, execution::outstanding_work.tracked))
{
}
const executor_type& get_executor() const noexcept
{
return executor_;
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor&, Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(
asio::require(executor_, execution::blocking.never),
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
asio::prefer(executor_,
execution::allocator(allocator)
).execute(static_cast<Function&&>(function));
}
private:
executor_type executor_;
};
#if !defined(ASIO_NO_TS_EXECUTORS)
template <typename Executor>
class channel_operation::handler_work_base<Executor,
enable_if_t<
!execution::is_executor<Executor>::value
>
>
{
public:
typedef Executor executor_type;
handler_work_base(int, const Executor& ex)
: work_(ex)
{
}
executor_type get_executor() const noexcept
{
return work_.get_executor();
}
template <typename IoExecutor, typename Function, typename Handler>
void post(const IoExecutor&, Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
work_.get_executor().post(
static_cast<Function&&>(function), allocator);
}
template <typename Function, typename Handler>
void dispatch(Function& function, Handler& handler)
{
associated_allocator_t<Handler> allocator =
(get_associated_allocator)(handler);
work_.get_executor().dispatch(
static_cast<Function&&>(function), allocator);
}
private:
executor_work_guard<Executor> work_;
};
#endif // !defined(ASIO_NO_TS_EXECUTORS)
template <typename Handler, typename IoExecutor, typename>
class channel_operation::handler_work :
channel_operation::handler_work_base<IoExecutor>,
channel_operation::handler_work_base<
associated_executor_t<Handler, IoExecutor>, IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
typedef channel_operation::handler_work_base<
associated_executor_t<Handler, IoExecutor>, IoExecutor>
base2_type;
handler_work(Handler& handler, const IoExecutor& io_ex) noexcept
: base1_type(0, io_ex),
base2_type(0, (get_associated_executor)(handler, io_ex))
{
}
template <typename Function>
void post(Function& function, Handler& handler)
{
base2_type::post(base1_type::get_executor(), function, handler);
}
template <typename Function>
void dispatch(Function& function, Handler& handler)
{
base2_type::dispatch(function, handler);
}
template <typename Function>
void immediate(Function& function, Handler& handler, ...)
{
typedef associated_immediate_executor_t<Handler,
typename base1_type::executor_type> immediate_ex_type;
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
handler, base1_type::get_executor());
(asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
immediate_ex))(static_cast<Function&&>(function));
}
template <typename Function>
void immediate(Function& function, Handler&,
enable_if_t<
is_same<
typename associated_immediate_executor<
conditional_t<false, Function, Handler>,
typename base1_type::executor_type>::
asio_associated_immediate_executor_is_unspecialised,
void
>::value
>*)
{
(asio::detail::initiate_post_with_executor<
typename base1_type::executor_type>(
base1_type::get_executor()))(
static_cast<Function&&>(function));
}
};
template <typename Handler, typename IoExecutor>
class channel_operation::handler_work<
Handler, IoExecutor,
enable_if_t<
is_same<
typename associated_executor<Handler,
IoExecutor>::asio_associated_executor_is_unspecialised,
void
>::value
>
> : handler_work_base<IoExecutor>
{
public:
typedef channel_operation::handler_work_base<IoExecutor> base1_type;
handler_work(Handler&, const IoExecutor& io_ex) noexcept
: base1_type(0, io_ex)
{
}
template <typename Function>
void post(Function& function, Handler& handler)
{
base1_type::post(base1_type::get_executor(), function, handler);
}
template <typename Function>
void dispatch(Function& function, Handler& handler)
{
base1_type::dispatch(function, handler);
}
template <typename Function>
void immediate(Function& function, Handler& handler, ...)
{
typedef associated_immediate_executor_t<Handler,
typename base1_type::executor_type> immediate_ex_type;
immediate_ex_type immediate_ex = (get_associated_immediate_executor)(
handler, base1_type::get_executor());
(asio::detail::initiate_dispatch_with_executor<immediate_ex_type>(
immediate_ex))(static_cast<Function&&>(function));
}
template <typename Function>
void immediate(Function& function, Handler& handler,
enable_if_t<
is_same<
typename associated_immediate_executor<
conditional_t<false, Function, Handler>,
typename base1_type::executor_type>::
asio_associated_immediate_executor_is_unspecialised,
void
>::value
>*)
{
base1_type::post(base1_type::get_executor(), function, handler);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP

View File

@@ -0,0 +1,126 @@
//
// experimental/detail/channel_receive_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/completion_handler.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/error.hpp"
#include "asio/experimental/detail/channel_operation.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_receive : public channel_operation
{
public:
void immediate(Payload payload)
{
func_(this, immediate_op, &payload);
}
void post(Payload payload)
{
func_(this, post_op, &payload);
}
void dispatch(Payload payload)
{
func_(this, dispatch_op, &payload);
}
protected:
channel_receive(func_type func)
: channel_operation(func)
{
}
};
template <typename Payload, typename Handler, typename IoExecutor>
class channel_receive_op : public channel_receive<Payload>
{
public:
ASIO_DEFINE_HANDLER_PTR(channel_receive_op);
template <typename... Args>
channel_receive_op(Handler& handler, const IoExecutor& io_ex)
: channel_receive<Payload>(&channel_receive_op::do_action),
handler_(static_cast<Handler&&>(handler)),
work_(handler_, io_ex)
{
}
static void do_action(channel_operation* base,
channel_operation::action a, void* v)
{
// Take ownership of the operation object.
channel_receive_op* o(static_cast<channel_receive_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
static_cast<channel_operation::handler_work<Handler, IoExecutor>&&>(
o->work_));
// Make a copy of the handler so that the memory can be deallocated before
// the handler is posted. Even if we're not about to post the handler, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
if (a != channel_operation::destroy_op)
{
Payload* payload = static_cast<Payload*>(v);
asio::detail::completion_payload_handler<Payload, Handler> handler(
static_cast<Payload&&>(*payload), o->handler_);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
ASIO_HANDLER_INVOCATION_BEGIN(());
if (a == channel_operation::immediate_op)
w.immediate(handler, handler.handler_, 0);
else if (a == channel_operation::dispatch_op)
w.dispatch(handler, handler.handler_);
else
w.post(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
else
{
asio::detail::binder0<Handler> handler(o->handler_);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_RECEIVE_OP_HPP

View File

@@ -0,0 +1,200 @@
//
// experimental/detail/channel_send_functions.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/async_result.hpp"
#include "asio/detail/completion_message.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/error_code.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Derived, typename Executor, typename... Signatures>
class channel_send_functions;
template <typename Derived, typename Executor, typename R, typename... Args>
class channel_send_functions<Derived, Executor, R(Args...)>
{
public:
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send_via_dispatch(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, true, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n_via_dispatch(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, true, static_cast<Args2&&>(args)...);
}
template <
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
-> decltype(
async_initiate<CompletionToken, void (asio::error_code)>(
declval<typename conditional_t<false, CompletionToken,
Derived>::initiate_async_send>(), token,
declval<typename conditional_t<false, CompletionToken,
Derived>::payload_type>()))
{
typedef typename Derived::payload_type payload_type;
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (asio::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, static_cast<Args&&>(args)...)));
}
};
template <typename Derived, typename Executor,
typename R, typename... Args, typename... Signatures>
class channel_send_functions<Derived, Executor, R(Args...), Signatures...> :
public channel_send_functions<Derived, Executor, Signatures...>
{
public:
using channel_send_functions<Derived, Executor, Signatures...>::try_send;
using channel_send_functions<Derived, Executor, Signatures...>::async_send;
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
bool
> try_send_via_dispatch(Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send<message_type>(
self->impl_, true, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, false, static_cast<Args2&&>(args)...);
}
template <typename... Args2>
enable_if_t<
is_constructible<asio::detail::completion_message<R(Args...)>,
int, Args2...>::value,
std::size_t
> try_send_n_via_dispatch(std::size_t count, Args2&&... args)
{
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return self->service_->template try_send_n<message_type>(
self->impl_, count, true, static_cast<Args2&&>(args)...);
}
template <
ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
auto async_send(Args... args,
CompletionToken&& token
ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
-> decltype(
async_initiate<CompletionToken, void (asio::error_code)>(
declval<typename conditional_t<false, CompletionToken,
Derived>::initiate_async_send>(), token,
declval<typename conditional_t<false, CompletionToken,
Derived>::payload_type>()))
{
typedef typename Derived::payload_type payload_type;
typedef asio::detail::completion_message<R(Args...)> message_type;
Derived* self = static_cast<Derived*>(this);
return async_initiate<CompletionToken, void (asio::error_code)>(
typename Derived::initiate_async_send(self), token,
payload_type(message_type(0, static_cast<Args&&>(args)...)));
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP

View File

@@ -0,0 +1,147 @@
//
// experimental/detail/channel_send_op.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/bind_handler.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/error.hpp"
#include "asio/experimental/channel_error.hpp"
#include "asio/experimental/detail/channel_operation.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Payload>
class channel_send : public channel_operation
{
public:
Payload get_payload()
{
return static_cast<Payload&&>(payload_);
}
void immediate()
{
func_(this, immediate_op, 0);
}
void post()
{
func_(this, post_op, 0);
}
void cancel()
{
func_(this, cancel_op, 0);
}
void close()
{
func_(this, close_op, 0);
}
protected:
channel_send(func_type func, Payload&& payload)
: channel_operation(func),
payload_(static_cast<Payload&&>(payload))
{
}
private:
Payload payload_;
};
template <typename Payload, typename Handler, typename IoExecutor>
class channel_send_op : public channel_send<Payload>
{
public:
ASIO_DEFINE_HANDLER_PTR(channel_send_op);
channel_send_op(Payload&& payload,
Handler& handler, const IoExecutor& io_ex)
: channel_send<Payload>(&channel_send_op::do_action,
static_cast<Payload&&>(payload)),
handler_(static_cast<Handler&&>(handler)),
work_(handler_, io_ex)
{
}
static void do_action(channel_operation* base,
channel_operation::action a, void*)
{
// Take ownership of the operation object.
channel_send_op* o(static_cast<channel_send_op*>(base));
ptr p = { asio::detail::addressof(o->handler_), o, o };
ASIO_HANDLER_COMPLETION((*o));
// Take ownership of the operation's outstanding work.
channel_operation::handler_work<Handler, IoExecutor> w(
static_cast<channel_operation::handler_work<Handler, IoExecutor>&&>(
o->work_));
asio::error_code ec;
switch (a)
{
case channel_operation::cancel_op:
ec = error::channel_cancelled;
break;
case channel_operation::close_op:
ec = error::channel_closed;
break;
default:
break;
}
// Make a copy of the handler so that the memory can be deallocated before
// the handler is posted. Even if we're not about to post the handler, a
// sub-object of the handler may be the true owner of the memory associated
// with the handler. Consequently, a local copy of the handler is required
// to ensure that any owning sub-object remains valid until after we have
// deallocated the memory here.
asio::detail::binder1<Handler, asio::error_code>
handler(o->handler_, ec);
p.h = asio::detail::addressof(handler.handler_);
p.reset();
// Post the completion if required.
if (a != channel_operation::destroy_op)
{
ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_));
if (a == channel_operation::immediate_op)
w.immediate(handler, handler.handler_, 0);
else
w.post(handler, handler.handler_);
ASIO_HANDLER_INVOCATION_END;
}
}
private:
Handler handler_;
channel_operation::handler_work<Handler, IoExecutor> work_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_OP_HPP

View File

@@ -0,0 +1,679 @@
//
// experimental/detail/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/associated_cancellation_slot.hpp"
#include "asio/cancellation_type.hpp"
#include "asio/detail/completion_message.hpp"
#include "asio/detail/completion_payload.hpp"
#include "asio/detail/completion_payload_handler.hpp"
#include "asio/detail/mutex.hpp"
#include "asio/detail/op_queue.hpp"
#include "asio/execution_context.hpp"
#include "asio/experimental/detail/channel_receive_op.hpp"
#include "asio/experimental/detail/channel_send_op.hpp"
#include "asio/experimental/detail/has_signature.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
class channel_service
: public asio::detail::execution_context_service_base<
channel_service<Mutex>>
{
public:
// Possible states for a channel end.
enum state
{
buffer = 0,
waiter = 1,
block = 2,
closed = 3
};
// The base implementation type of all channels.
struct base_implementation_type
{
// Default constructor.
base_implementation_type()
: receive_state_(block),
send_state_(block),
max_buffer_size_(0),
next_(0),
prev_(0)
{
}
// The current state of the channel.
state receive_state_ : 16;
state send_state_ : 16;
// The maximum number of elements that may be buffered in the channel.
std::size_t max_buffer_size_;
// The operations that are waiting on the channel.
asio::detail::op_queue<channel_operation> waiters_;
// Pointers to adjacent channel implementations in linked list.
base_implementation_type* next_;
base_implementation_type* prev_;
// The mutex type to protect the internal implementation.
mutable Mutex mutex_;
};
// The implementation for a specific value type.
template <typename Traits, typename... Signatures>
struct implementation_type;
// Constructor.
channel_service(asio::execution_context& ctx);
// Destroy all user-defined handler objects owned by the service.
void shutdown();
// Construct a new channel implementation.
void construct(base_implementation_type& impl, std::size_t max_buffer_size);
// Destroy a channel implementation.
template <typename Traits, typename... Signatures>
void destroy(implementation_type<Traits, Signatures...>& impl);
// Move-construct a new channel implementation.
template <typename Traits, typename... Signatures>
void move_construct(implementation_type<Traits, Signatures...>& impl,
implementation_type<Traits, Signatures...>& other_impl);
// Move-assign from another channel implementation.
template <typename Traits, typename... Signatures>
void move_assign(implementation_type<Traits, Signatures...>& impl,
channel_service& other_service,
implementation_type<Traits, Signatures...>& other_impl);
// Get the capacity of the channel.
std::size_t capacity(
const base_implementation_type& impl) const noexcept;
// Determine whether the channel is open.
bool is_open(const base_implementation_type& impl) const noexcept;
// Reset the channel to its initial state.
template <typename Traits, typename... Signatures>
void reset(implementation_type<Traits, Signatures...>& impl);
// Close the channel.
template <typename Traits, typename... Signatures>
void close(implementation_type<Traits, Signatures...>& impl);
// Cancel all operations associated with the channel.
template <typename Traits, typename... Signatures>
void cancel(implementation_type<Traits, Signatures...>& impl);
// Cancel the operation associated with the channel that has the given key.
template <typename Traits, typename... Signatures>
void cancel_by_key(implementation_type<Traits, Signatures...>& impl,
void* cancellation_key);
// Determine whether a value can be read from the channel without blocking.
bool ready(const base_implementation_type& impl) const noexcept;
// Synchronously send a new value into the channel.
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
bool try_send(implementation_type<Traits, Signatures...>& impl,
bool via_dispatch, Args&&... args);
// Synchronously send a number of new values into the channel.
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
std::size_t try_send_n(implementation_type<Traits, Signatures...>& impl,
std::size_t count, bool via_dispatch, Args&&... args);
// Asynchronously send a new value into the channel.
template <typename Traits, typename... Signatures,
typename Handler, typename IoExecutor>
void async_send(implementation_type<Traits, Signatures...>& impl,
typename implementation_type<Traits,
Signatures...>::payload_type&& payload,
Handler& handler, const IoExecutor& io_ex)
{
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// Allocate and construct an operation to wrap the handler.
typedef channel_send_op<
typename implementation_type<Traits, Signatures...>::payload_type,
Handler, IoExecutor> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(static_cast<typename implementation_type<
Traits, Signatures...>::payload_type&&>(payload), handler, io_ex);
// Optionally register for per-operation cancellation.
if (slot.is_connected())
{
p.p->cancellation_key_ =
&slot.template emplace<op_cancellation<Traits, Signatures...>>(
this, &impl);
}
ASIO_HANDLER_CREATION((this->context(), *p.p,
"channel", &impl, 0, "async_send"));
start_send_op(impl, p.p);
p.v = p.p = 0;
}
// Synchronously receive a value from the channel.
template <typename Traits, typename... Signatures, typename Handler>
bool try_receive(implementation_type<Traits, Signatures...>& impl,
Handler&& handler);
// Asynchronously receive a value from the channel.
template <typename Traits, typename... Signatures,
typename Handler, typename IoExecutor>
void async_receive(implementation_type<Traits, Signatures...>& impl,
Handler& handler, const IoExecutor& io_ex)
{
associated_cancellation_slot_t<Handler> slot
= asio::get_associated_cancellation_slot(handler);
// Allocate and construct an operation to wrap the handler.
typedef channel_receive_op<
typename implementation_type<Traits, Signatures...>::payload_type,
Handler, IoExecutor> op;
typename op::ptr p = { asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(handler, io_ex);
// Optionally register for per-operation cancellation.
if (slot.is_connected())
{
p.p->cancellation_key_ =
&slot.template emplace<op_cancellation<Traits, Signatures...>>(
this, &impl);
}
ASIO_HANDLER_CREATION((this->context(), *p.p,
"channel", &impl, 0, "async_receive"));
start_receive_op(impl, p.p);
p.v = p.p = 0;
}
private:
// Helper function object to handle a closed notification.
template <typename Payload, typename Signature>
struct post_receive
{
explicit post_receive(channel_receive<Payload>* op)
: op_(op)
{
}
template <typename... Args>
void operator()(Args&&... args)
{
op_->post(
asio::detail::completion_message<Signature>(0,
static_cast<Args&&>(args)...));
}
channel_receive<Payload>* op_;
};
// Destroy a base channel implementation.
void base_destroy(base_implementation_type& impl);
// Helper function to start an asynchronous put operation.
template <typename Traits, typename... Signatures>
void start_send_op(implementation_type<Traits, Signatures...>& impl,
channel_send<typename implementation_type<
Traits, Signatures...>::payload_type>* send_op);
// Helper function to start an asynchronous get operation.
template <typename Traits, typename... Signatures>
void start_receive_op(implementation_type<Traits, Signatures...>& impl,
channel_receive<typename implementation_type<
Traits, Signatures...>::payload_type>* receive_op);
// Helper class used to implement per-operation cancellation.
template <typename Traits, typename... Signatures>
class op_cancellation
{
public:
op_cancellation(channel_service* s,
implementation_type<Traits, Signatures...>* impl)
: service_(s),
impl_(impl)
{
}
void operator()(cancellation_type_t type)
{
if (!!(type &
(cancellation_type::terminal
| cancellation_type::partial
| cancellation_type::total)))
{
service_->cancel_by_key(*impl_, this);
}
}
private:
channel_service* service_;
implementation_type<Traits, Signatures...>* impl_;
};
// Mutex to protect access to the linked list of implementations.
asio::detail::mutex mutex_;
// The head of a linked list of all implementations.
base_implementation_type* impl_list_;
};
// The implementation for a specific value type.
template <typename Mutex>
template <typename Traits, typename... Signatures>
struct channel_service<Mutex>::implementation_type : base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<Signatures...>::other traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
Signatures...
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...
>::value,
asio::detail::completion_payload<Signatures...>,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
Signatures...,
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
Signatures...,
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
buffer_ = static_cast<
typename traits_type::template container<payload_type>::type&&>(
other.buffer_);
other.buffer_clear();
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return buffer_.size();
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type payload)
{
buffer_.push_back(static_cast<payload_type&&>(payload));
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type payload)
{
std::size_t i = 0;
for (; i < count && buffer_.size() < this->max_buffer_size_; ++i)
buffer_.push_back(payload);
return i;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return static_cast<payload_type&&>(buffer_.front());
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
buffer_.pop_front();
}
// Clear all buffered values.
void buffer_clear()
{
buffer_.clear();
}
private:
// Buffered values.
typename traits_type::template container<payload_type>::type buffer_;
};
// The implementation for a void value type.
template <typename Mutex>
template <typename Traits, typename R>
struct channel_service<Mutex>::implementation_type<Traits, R()>
: channel_service::base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<R()>::other traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
R()
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R()
>::value,
asio::detail::completion_payload<R()>,
asio::detail::completion_payload<
R(),
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(),
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
R(),
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
R(),
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Construct with empty buffer.
implementation_type()
: buffer_(0)
{
}
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
buffer_ = other.buffer_;
other.buffer_ = 0;
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return buffer_;
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type)
{
++buffer_;
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type)
{
std::size_t available = this->max_buffer_size_ - buffer_;
count = (count < available) ? count : available;
buffer_ += count;
return count;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return payload_type(asio::detail::completion_message<R()>(0));
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
--buffer_;
}
// Clear all values from the buffer.
void buffer_clear()
{
buffer_ = 0;
}
private:
// Number of buffered "values".
std::size_t buffer_;
};
// The implementation for an error_code signature.
template <typename Mutex>
template <typename Traits, typename R>
struct channel_service<Mutex>::implementation_type<
Traits, R(asio::error_code)>
: channel_service::base_implementation_type
{
// The traits type associated with the channel.
typedef typename Traits::template rebind<R(asio::error_code)>::other
traits_type;
// Type of an element stored in the buffer.
typedef conditional_t<
has_signature<
typename traits_type::receive_cancelled_signature,
R(asio::error_code)
>::value,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(asio::error_code)
>::value,
asio::detail::completion_payload<R(asio::error_code)>,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_closed_signature
>
>,
conditional_t<
has_signature<
typename traits_type::receive_closed_signature,
R(asio::error_code),
typename traits_type::receive_cancelled_signature
>::value,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_cancelled_signature
>,
asio::detail::completion_payload<
R(asio::error_code),
typename traits_type::receive_cancelled_signature,
typename traits_type::receive_closed_signature
>
>
> payload_type;
// Construct with empty buffer.
implementation_type()
: size_(0)
{
first_.count_ = 0;
}
// Move from another buffer.
void buffer_move_from(implementation_type& other)
{
size_ = other.size_;
other.size_ = 0;
first_ = other.first_;
other.first_.count_ = 0;
rest_ = static_cast<
typename traits_type::template container<buffered_value>::type&&>(
other.rest_);
other.buffer_clear();
}
// Get number of buffered elements.
std::size_t buffer_size() const
{
return size_;
}
// Push a new value to the back of the buffer.
void buffer_push(payload_type payload)
{
buffered_value& last = rest_.empty() ? first_ : rest_.back();
if (last.count_ == 0)
{
value_handler handler{last.value_};
payload.receive(handler);
last.count_ = 1;
}
else
{
asio::error_code value{last.value_};
value_handler handler{value};
payload.receive(handler);
if (last.value_ == value)
++last.count_;
else
rest_.push_back({value, 1});
}
++size_;
}
// Push new values to the back of the buffer.
std::size_t buffer_push_n(std::size_t count, payload_type payload)
{
std::size_t available = this->max_buffer_size_ - size_;
count = (count < available) ? count : available;
if (count > 0)
{
buffered_value& last = rest_.empty() ? first_ : rest_.back();
if (last.count_ == 0)
{
payload.receive(value_handler{last.value_});
last.count_ = count;
}
else
{
asio::error_code value{last.value_};
payload.receive(value_handler{value});
if (last.value_ == value)
last.count_ += count;
else
rest_.push_back({value, count});
}
size_ += count;
}
return count;
}
// Get the element at the front of the buffer.
payload_type buffer_front()
{
return payload_type({0, first_.value_});
}
// Pop a value from the front of the buffer.
void buffer_pop()
{
--size_;
if (--first_.count_ == 0 && !rest_.empty())
{
first_ = rest_.front();
rest_.pop_front();
}
}
// Clear all values from the buffer.
void buffer_clear()
{
size_ = 0;
first_.count_ = 0;
rest_.clear();
}
private:
struct buffered_value
{
asio::error_code value_;
std::size_t count_;
};
struct value_handler
{
asio::error_code& target_;
template <typename... Args>
void operator()(const asio::error_code& value, Args&&...)
{
target_ = value;
}
};
buffered_value& last_value()
{
return rest_.empty() ? first_ : rest_.back();
}
// Total number of buffered values.
std::size_t size_;
// The first buffered value is maintained as a separate data member to avoid
// allocating space in the container in the common case.
buffered_value first_;
// The rest of the buffered values.
typename traits_type::template container<buffered_value>::type rest_;
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/detail/impl/channel_service.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,169 @@
//
// experimental/detail/coro_completion_handler.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot 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 ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/deferred.hpp"
#include "asio/experimental/coro.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Promise, typename... Args>
struct coro_completion_handler
{
coro_completion_handler(coroutine_handle<Promise> h,
std::optional<std::tuple<Args...>>& result)
: self(h),
result(result)
{
}
coro_completion_handler(coro_completion_handler&&) = default;
coroutine_handle<Promise> self;
std::optional<std::tuple<Args...>>& result;
using promise_type = Promise;
void operator()(Args... args)
{
result.emplace(std::move(args)...);
self.resume();
}
using allocator_type = typename promise_type::allocator_type;
allocator_type get_allocator() const noexcept
{
return self.promise().get_allocator();
}
using executor_type = typename promise_type::executor_type;
executor_type get_executor() const noexcept
{
return self.promise().get_executor();
}
using cancellation_slot_type = typename promise_type::cancellation_slot_type;
cancellation_slot_type get_cancellation_slot() const noexcept
{
return self.promise().get_cancellation_slot();
}
};
template <typename Signature>
struct coro_completion_handler_type;
template <typename... Args>
struct coro_completion_handler_type<void(Args...)>
{
using type = std::tuple<Args...>;
template <typename Promise>
using completion_handler = coro_completion_handler<Promise, Args...>;
};
template <typename Signature>
using coro_completion_handler_type_t =
typename coro_completion_handler_type<Signature>::type;
inline void coro_interpret_result(std::tuple<>&&)
{
}
template <typename... Args>
inline auto coro_interpret_result(std::tuple<Args...>&& args)
{
return std::move(args);
}
template <typename... Args>
auto coro_interpret_result(std::tuple<std::exception_ptr, Args...>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
return std::apply(
[](auto, auto&&... rest)
{
return std::make_tuple(std::move(rest)...);
}, std::move(args));
}
template <typename... Args>
auto coro_interpret_result(
std::tuple<asio::error_code, Args...>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
return std::apply(
[](auto, auto&&... rest)
{
return std::make_tuple(std::move(rest)...);
}, std::move(args));
}
template <typename Arg>
inline auto coro_interpret_result(std::tuple<Arg>&& args)
{
return std::get<0>(std::move(args));
}
template <typename Arg>
auto coro_interpret_result(std::tuple<std::exception_ptr, Arg>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
return std::get<1>(std::move(args));
}
inline auto coro_interpret_result(
std::tuple<asio::error_code>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
}
inline auto coro_interpret_result(std::tuple<std::exception_ptr>&& args)
{
if (std::get<0>(args))
std::rethrow_exception(std::get<0>(args));
}
template <typename Arg>
auto coro_interpret_result(std::tuple<asio::error_code, Arg>&& args)
{
if (std::get<0>(args))
asio::detail::throw_exception(
asio::system_error(std::get<0>(args)));
return std::get<1>(std::move(args));
}
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_CORO_COMPLETION_HANDLER_HPP

View File

@@ -0,0 +1,141 @@
//
// experimental/detail/coro_promise_allocator.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot 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 ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#define ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP
#include "asio/detail/config.hpp"
#include <limits>
#include "asio/experimental/coro_traits.hpp"
namespace asio {
namespace experimental {
namespace detail {
/// Allocate the memory and put the allocator behind the coro memory
template <typename AllocatorType>
void* allocate_coroutine(const std::size_t size, AllocatorType alloc_)
{
using alloc_type = typename std::allocator_traits<AllocatorType>::template
rebind_alloc<unsigned char>;
alloc_type alloc{alloc_};
const auto align_needed = size % alignof(alloc_type);
const auto align_offset = align_needed != 0
? alignof(alloc_type) - align_needed : 0ull;
const auto alloc_size = size + sizeof(alloc_type) + align_offset;
const auto raw =
std::allocator_traits<alloc_type>::allocate(alloc, alloc_size);
new(raw + size + align_offset) alloc_type(std::move(alloc));
return raw;
}
/// Deallocate the memory and destroy the allocator in the coro memory.
template <typename AllocatorType>
void deallocate_coroutine(void* raw_, const std::size_t size)
{
using alloc_type = typename std::allocator_traits<AllocatorType>::template
rebind_alloc<unsigned char>;
const auto raw = static_cast<unsigned char *>(raw_);
const auto align_needed = size % alignof(alloc_type);
const auto align_offset = align_needed != 0
? alignof(alloc_type) - align_needed : 0ull;
const auto alloc_size = size + sizeof(alloc_type) + align_offset;
auto alloc_p = reinterpret_cast<alloc_type *>(raw + size + align_offset);
auto alloc = std::move(*alloc_p);
alloc_p->~alloc_type();
std::allocator_traits<alloc_type>::deallocate(alloc, raw, alloc_size);
}
template <typename T>
constexpr std::size_t variadic_first(std::size_t = 0u)
{
return std::numeric_limits<std::size_t>::max();
}
template <typename T, typename First, typename... Args>
constexpr std::size_t variadic_first(std::size_t pos = 0u)
{
if constexpr (std::is_same_v<std::decay_t<First>, T>)
return pos;
else
return variadic_first<T, Args...>(pos+1);
}
template <std::size_t Idx, typename First, typename... Args>
requires (Idx <= sizeof...(Args))
constexpr decltype(auto) get_variadic(First&& first, Args&&... args)
{
if constexpr (Idx == 0u)
return static_cast<First>(first);
else
return get_variadic<Idx-1u>(static_cast<Args>(args)...);
}
template <std::size_t Idx>
constexpr decltype(auto) get_variadic();
template <typename Allocator>
struct coro_promise_allocator
{
using allocator_type = Allocator;
allocator_type get_allocator() const {return alloc_;}
template <typename... Args>
void* operator new(std::size_t size, Args & ... args)
{
return allocate_coroutine(size,
get_variadic<variadic_first<std::allocator_arg_t,
std::decay_t<Args>...>() + 1u>(args...));
}
void operator delete(void* raw, std::size_t size)
{
deallocate_coroutine<allocator_type>(raw, size);
}
template <typename... Args>
coro_promise_allocator(Args&& ... args)
: alloc_(
get_variadic<variadic_first<std::allocator_arg_t,
std::decay_t<Args>...>() + 1u>(args...))
{
}
private:
allocator_type alloc_;
};
template <>
struct coro_promise_allocator<std::allocator<void>>
{
using allocator_type = std::allocator<void>;
template <typename... Args>
coro_promise_allocator(Args&&...)
{
}
allocator_type get_allocator() const
{
return {};
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#endif // ASIO_EXPERIMENTAL_DETAIL_CORO_PROMISE_ALLOCATOR_HPP

View File

@@ -0,0 +1,54 @@
//
// experimental/detail/has_signature.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename S, typename... Signatures>
struct has_signature;
template <typename S, typename... Signatures>
struct has_signature;
template <typename S>
struct has_signature<S> : false_type
{
};
template <typename S, typename... Signatures>
struct has_signature<S, S, Signatures...> : true_type
{
};
template <typename S, typename Head, typename... Tail>
struct has_signature<S, Head, Tail...> : has_signature<S, Tail...>
{
};
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_HAS_SIGNATURE_HPP

View File

@@ -0,0 +1,623 @@
//
// experimental/detail/impl/channel_service.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
namespace detail {
template <typename Mutex>
inline channel_service<Mutex>::channel_service(
asio::execution_context& ctx)
: asio::detail::execution_context_service_base<channel_service>(ctx),
mutex_(),
impl_list_(0)
{
}
template <typename Mutex>
inline void channel_service<Mutex>::shutdown()
{
// Abandon all pending operations.
asio::detail::op_queue<channel_operation> ops;
asio::detail::mutex::scoped_lock lock(mutex_);
base_implementation_type* impl = impl_list_;
while (impl)
{
ops.push(impl->waiters_);
impl = impl->next_;
}
}
template <typename Mutex>
inline void channel_service<Mutex>::construct(
channel_service<Mutex>::base_implementation_type& impl,
std::size_t max_buffer_size)
{
impl.max_buffer_size_ = max_buffer_size;
impl.receive_state_ = block;
impl.send_state_ = max_buffer_size ? buffer : block;
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::destroy(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
cancel(impl);
base_destroy(impl);
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::move_construct(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_service<Mutex>::implementation_type<
Traits, Signatures...>& other_impl)
{
impl.max_buffer_size_ = other_impl.max_buffer_size_;
impl.receive_state_ = other_impl.receive_state_;
other_impl.receive_state_ = block;
impl.send_state_ = other_impl.send_state_;
other_impl.send_state_ = other_impl.max_buffer_size_ ? buffer : block;
impl.buffer_move_from(other_impl);
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::move_assign(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_service& other_service,
channel_service<Mutex>::implementation_type<
Traits, Signatures...>& other_impl)
{
cancel(impl);
if (this != &other_service)
{
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
impl.max_buffer_size_ = other_impl.max_buffer_size_;
impl.receive_state_ = other_impl.receive_state_;
other_impl.receive_state_ = block;
impl.send_state_ = other_impl.send_state_;
other_impl.send_state_ = other_impl.max_buffer_size_ ? buffer : block;
impl.buffer_move_from(other_impl);
if (this != &other_service)
{
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(other_service.mutex_);
impl.next_ = other_service.impl_list_;
impl.prev_ = 0;
if (other_service.impl_list_)
other_service.impl_list_->prev_ = &impl;
other_service.impl_list_ = &impl;
}
}
template <typename Mutex>
inline void channel_service<Mutex>::base_destroy(
channel_service<Mutex>::base_implementation_type& impl)
{
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
template <typename Mutex>
inline std::size_t channel_service<Mutex>::capacity(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.max_buffer_size_;
}
template <typename Mutex>
inline bool channel_service<Mutex>::is_open(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.send_state_ != closed;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::reset(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
cancel(impl);
typename Mutex::scoped_lock lock(impl.mutex_);
impl.receive_state_ = block;
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
impl.buffer_clear();
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::close(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
if (impl.receive_state_ == block)
{
while (channel_operation* op = impl.waiters_.front())
{
impl.waiters_.pop();
traits_type::invoke_receive_closed(
post_receive<payload_type,
typename traits_type::receive_closed_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
impl.send_state_ = closed;
if (impl.receive_state_ != buffer)
impl.receive_state_ = closed;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::cancel(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
while (channel_operation* op = impl.waiters_.front())
{
if (impl.send_state_ == block)
{
impl.waiters_.pop();
static_cast<channel_send<payload_type>*>(op)->cancel();
}
else
{
impl.waiters_.pop();
traits_type::invoke_receive_cancelled(
post_receive<payload_type,
typename traits_type::receive_cancelled_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
if (impl.receive_state_ == waiter)
impl.receive_state_ = block;
if (impl.send_state_ == waiter)
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::cancel_by_key(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
void* cancellation_key)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
asio::detail::op_queue<channel_operation> other_ops;
while (channel_operation* op = impl.waiters_.front())
{
if (op->cancellation_key_ == cancellation_key)
{
if (impl.send_state_ == block)
{
impl.waiters_.pop();
static_cast<channel_send<payload_type>*>(op)->cancel();
}
else
{
impl.waiters_.pop();
traits_type::invoke_receive_cancelled(
post_receive<payload_type,
typename traits_type::receive_cancelled_signature>(
static_cast<channel_receive<payload_type>*>(op)));
}
}
else
{
impl.waiters_.pop();
other_ops.push(op);
}
}
impl.waiters_.push(other_ops);
if (impl.waiters_.empty())
{
if (impl.receive_state_ == waiter)
impl.receive_state_ = block;
if (impl.send_state_ == waiter)
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
}
}
template <typename Mutex>
inline bool channel_service<Mutex>::ready(
const channel_service<Mutex>::base_implementation_type& impl)
const noexcept
{
typename Mutex::scoped_lock lock(impl.mutex_);
return impl.receive_state_ != block;
}
template <typename Mutex>
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
bool channel_service<Mutex>::try_send(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
bool via_dispatch, Args&&... args)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.send_state_)
{
case block:
{
return false;
}
case buffer:
{
impl.buffer_push(Message(0, static_cast<Args&&>(args)...));
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
return true;
}
case waiter:
{
payload_type payload(Message(0, static_cast<Args&&>(args)...));
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
lock.unlock();
if (via_dispatch)
receive_op->dispatch(static_cast<payload_type&&>(payload));
else
receive_op->post(static_cast<payload_type&&>(payload));
return true;
}
case closed:
default:
{
return false;
}
}
}
template <typename Mutex>
template <typename Message, typename Traits,
typename... Signatures, typename... Args>
std::size_t channel_service<Mutex>::try_send_n(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
std::size_t count, bool via_dispatch, Args&&... args)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
if (count == 0)
return 0;
switch (impl.send_state_)
{
case block:
return 0;
case buffer:
case waiter:
break;
case closed:
default:
return 0;
}
payload_type payload(Message(0, static_cast<Args&&>(args)...));
for (std::size_t i = 0; i < count; ++i)
{
switch (impl.send_state_)
{
case block:
{
return i;
}
case buffer:
{
i += impl.buffer_push_n(count - i,
static_cast<payload_type&&>(payload));
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
return i;
}
case waiter:
{
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
lock.unlock();
if (via_dispatch)
receive_op->dispatch(payload);
else
receive_op->post(payload);
break;
}
case closed:
default:
{
return i;
}
}
}
return count;
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::start_send_op(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_send<typename implementation_type<
Traits, Signatures...>::payload_type>* send_op)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.send_state_)
{
case block:
{
impl.waiters_.push(send_op);
if (impl.receive_state_ == block)
impl.receive_state_ = waiter;
return;
}
case buffer:
{
impl.buffer_push(send_op->get_payload());
impl.receive_state_ = buffer;
if (impl.buffer_size() == impl.max_buffer_size_)
impl.send_state_ = block;
send_op->immediate();
break;
}
case waiter:
{
channel_receive<payload_type>* receive_op =
static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
impl.waiters_.pop();
if (impl.waiters_.empty())
impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
receive_op->post(send_op->get_payload());
send_op->immediate();
break;
}
case closed:
default:
{
send_op->close();
break;
}
}
}
template <typename Mutex>
template <typename Traits, typename... Signatures, typename Handler>
bool channel_service<Mutex>::try_receive(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
Handler&& handler)
{
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.receive_state_)
{
case block:
{
return false;
}
case buffer:
{
payload_type payload(impl.buffer_front());
if (channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front()))
{
impl.buffer_pop();
impl.buffer_push(send_op->get_payload());
impl.waiters_.pop();
send_op->post();
}
else
{
impl.buffer_pop();
if (impl.buffer_size() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
impl.send_state_ = (impl.send_state_ == closed) ? closed : buffer;
}
lock.unlock();
asio::detail::non_const_lvalue<Handler> handler2(handler);
asio::detail::completion_payload_handler<
payload_type, decay_t<Handler>>(
static_cast<payload_type&&>(payload), handler2.value)();
return true;
}
case waiter:
{
channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front());
payload_type payload = send_op->get_payload();
impl.waiters_.pop();
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
send_op->post();
lock.unlock();
asio::detail::non_const_lvalue<Handler> handler2(handler);
asio::detail::completion_payload_handler<
payload_type, decay_t<Handler>>(
static_cast<payload_type&&>(payload), handler2.value)();
return true;
}
case closed:
default:
{
return false;
}
}
}
template <typename Mutex>
template <typename Traits, typename... Signatures>
void channel_service<Mutex>::start_receive_op(
channel_service<Mutex>::implementation_type<Traits, Signatures...>& impl,
channel_receive<typename implementation_type<
Traits, Signatures...>::payload_type>* receive_op)
{
typedef typename implementation_type<Traits,
Signatures...>::traits_type traits_type;
typedef typename implementation_type<Traits,
Signatures...>::payload_type payload_type;
typename Mutex::scoped_lock lock(impl.mutex_);
switch (impl.receive_state_)
{
case block:
{
impl.waiters_.push(receive_op);
if (impl.send_state_ != closed)
impl.send_state_ = waiter;
return;
}
case buffer:
{
payload_type payload(
static_cast<payload_type&&>(impl.buffer_front()));
if (channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front()))
{
impl.buffer_pop();
impl.buffer_push(send_op->get_payload());
impl.waiters_.pop();
send_op->post();
}
else
{
impl.buffer_pop();
if (impl.buffer_size() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
impl.send_state_ = (impl.send_state_ == closed) ? closed : buffer;
}
receive_op->immediate(static_cast<payload_type&&>(payload));
break;
}
case waiter:
{
channel_send<payload_type>* send_op =
static_cast<channel_send<payload_type>*>(impl.waiters_.front());
payload_type payload = send_op->get_payload();
impl.waiters_.pop();
if (impl.waiters_.front() == 0)
impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
send_op->post();
receive_op->immediate(static_cast<payload_type&&>(payload));
break;
}
case closed:
default:
{
traits_type::invoke_receive_closed(
post_receive<payload_type,
typename traits_type::receive_closed_signature>(receive_op));
break;
}
}
}
} // namespace detail
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_EXPERIMENTAL_DETAIL_IMPL_CHANNEL_SERVICE_HPP

View File

@@ -0,0 +1,195 @@
//
// experimental/detail/partial_promise.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2021-2023 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot 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 ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#define ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP
#include "asio/detail/config.hpp"
#include "asio/append.hpp"
#include "asio/awaitable.hpp"
#include "asio/experimental/coro_traits.hpp"
#if defined(ASIO_HAS_STD_COROUTINE)
# include <coroutine>
#else // defined(ASIO_HAS_STD_COROUTINE)
# include <experimental/coroutine>
#endif // defined(ASIO_HAS_STD_COROUTINE)
namespace asio {
namespace experimental {
namespace detail {
#if defined(ASIO_HAS_STD_COROUTINE)
using std::coroutine_handle;
using std::coroutine_traits;
using std::suspend_never;
using std::suspend_always;
using std::noop_coroutine;
#else // defined(ASIO_HAS_STD_COROUTINE)
using std::experimental::coroutine_handle;
using std::experimental::coroutine_traits;
using std::experimental::suspend_never;
using std::experimental::suspend_always;
using std::experimental::noop_coroutine;
#endif // defined(ASIO_HAS_STD_COROUTINE)
struct partial_coro
{
coroutine_handle<void> handle{nullptr};
};
template <typename Allocator>
struct partial_promise_base
{
template <typename Executor, typename Token, typename... Args>
void* operator new(std::size_t size, Executor&, Token& tk, Args&...)
{
return allocate_coroutine<Allocator>(size, get_associated_allocator(tk));
}
void operator delete(void* raw, std::size_t size)
{
deallocate_coroutine<Allocator>(raw, size);
}
};
template <>
struct partial_promise_base<std::allocator<void>>
{
};
template <typename Allocator>
struct partial_promise : partial_promise_base<Allocator>
{
auto initial_suspend() noexcept
{
return asio::detail::suspend_always{};
}
auto final_suspend() noexcept
{
struct awaitable_t
{
partial_promise *p;
constexpr bool await_ready() noexcept { return true; }
auto await_suspend(asio::detail::coroutine_handle<>) noexcept
{
p->get_return_object().handle.destroy();
}
constexpr void await_resume() noexcept {}
};
return awaitable_t{this};
}
void return_void() {}
partial_coro get_return_object()
{
return partial_coro{coroutine_handle<partial_promise>::from_promise(*this)};
}
void unhandled_exception()
{
assert(false);
}
};
} // namespace detail
} // namespace experimental
} // namespace asio
#if defined(ASIO_HAS_STD_COROUTINE)
namespace std {
template <typename Executor, typename Completion, typename... Args>
struct coroutine_traits<
asio::experimental::detail::partial_coro,
Executor, Completion, Args...>
{
using promise_type =
asio::experimental::detail::partial_promise<
asio::associated_allocator_t<Completion>>;
};
} // namespace std
#else // defined(ASIO_HAS_STD_COROUTINE)
namespace std { namespace experimental {
template <typename Executor, typename Completion, typename... Args>
struct coroutine_traits<
asio::experimental::detail::partial_coro,
Executor, Completion, Args...>
{
using promise_type =
asio::experimental::detail::partial_promise<
asio::associated_allocator_t<Completion>>;
};
}} // namespace std::experimental
#endif // defined(ASIO_HAS_STD_COROUTINE)
namespace asio {
namespace experimental {
namespace detail {
template <execution::executor Executor,
typename CompletionToken, typename... Args>
partial_coro post_coroutine(Executor exec,
CompletionToken token, Args&&... args) noexcept
{
post(exec, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <detail::execution_context Context,
typename CompletionToken, typename... Args>
partial_coro post_coroutine(Context& ctx,
CompletionToken token, Args&&... args) noexcept
{
post(ctx, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <execution::executor Executor,
typename CompletionToken, typename... Args>
partial_coro dispatch_coroutine(Executor exec,
CompletionToken token, Args&&... args) noexcept
{
dispatch(exec, asio::append(std::move(token), std::move(args)...));
co_return;
}
template <detail::execution_context Context,
typename CompletionToken, typename... Args>
partial_coro dispatch_coroutine(Context& ctx,
CompletionToken token, Args &&... args) noexcept
{
dispatch(ctx, asio::append(std::move(token), std::move(args)...));
co_return;
}
} // namespace detail
} // namespace experimental
} // namespace asio
#endif // ASIO_EXPERIMENTAL_DETAIL_PARTIAL_PROMISE_HPP