Initial Commit
This commit is contained in:
361
include/asio/experimental/detail/channel_operation.hpp
Normal file
361
include/asio/experimental/detail/channel_operation.hpp
Normal 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
|
||||
126
include/asio/experimental/detail/channel_receive_op.hpp
Normal file
126
include/asio/experimental/detail/channel_receive_op.hpp
Normal 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
|
||||
200
include/asio/experimental/detail/channel_send_functions.hpp
Normal file
200
include/asio/experimental/detail/channel_send_functions.hpp
Normal 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
|
||||
147
include/asio/experimental/detail/channel_send_op.hpp
Normal file
147
include/asio/experimental/detail/channel_send_op.hpp
Normal 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
|
||||
679
include/asio/experimental/detail/channel_service.hpp
Normal file
679
include/asio/experimental/detail/channel_service.hpp
Normal 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
|
||||
169
include/asio/experimental/detail/coro_completion_handler.hpp
Normal file
169
include/asio/experimental/detail/coro_completion_handler.hpp
Normal 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
|
||||
141
include/asio/experimental/detail/coro_promise_allocator.hpp
Normal file
141
include/asio/experimental/detail/coro_promise_allocator.hpp
Normal 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
|
||||
54
include/asio/experimental/detail/has_signature.hpp
Normal file
54
include/asio/experimental/detail/has_signature.hpp
Normal 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
|
||||
623
include/asio/experimental/detail/impl/channel_service.hpp
Normal file
623
include/asio/experimental/detail/impl/channel_service.hpp
Normal 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
|
||||
195
include/asio/experimental/detail/partial_promise.hpp
Normal file
195
include/asio/experimental/detail/partial_promise.hpp
Normal 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
|
||||
Reference in New Issue
Block a user