Add experimental support for channels.
This adds experimental::channel and experimental::concurrent_channel.
Channels may be used to send completions as messages. For example:
// Create a channel with no buffer space.
channel<void(error_code, size_t)> ch(ctx);
// The call to try_send fails as there is no buffer
// space and no waiting receive operations.
bool ok = ch.try_send(asio::error::eof, 123);
assert(!ok);
// The async_send operation blocks until a receive
// operation consumes the message.
ch.async_send(asio::error::eof, 123,
[](error_code ec)
{
// ...
});
// The async_receive consumes the message. Both the
// async_send and async_receive operations complete
// immediately.
ch.async_receive(
[](error_code ec, size_t n)
{
// ...
});
diff --git a/asio/boostify.pl b/asio/boostify.pl
index 3142d4e..b2ded90 100755
--- a/asio/boostify.pl
+++ b/asio/boostify.pl
@@ -387,6 +387,7 @@
"include/asio/execution/impl",
"include/asio/experimental",
"include/asio/experimental/detail",
+ "include/asio/experimental/detail/impl",
"include/asio/experimental/impl",
"include/asio/generic",
"include/asio/generic/detail",
diff --git a/asio/include/Makefile.am b/asio/include/Makefile.am
index 6d01b96..2d59a99 100644
--- a/asio/include/Makefile.am
+++ b/asio/include/Makefile.am
@@ -379,16 +379,34 @@
asio/experimental/as_single.hpp \
asio/experimental/as_tuple.hpp \
asio/experimental/awaitable_operators.hpp \
+ asio/experimental/basic_channel.hpp \
+ asio/experimental/basic_concurrent_channel.hpp \
asio/experimental/cancellation_condition.hpp \
+ asio/experimental/channel.hpp \
+ asio/experimental/channel_error.hpp \
+ asio/experimental/channel_traits.hpp \
+ asio/experimental/co_spawn.hpp \
+ asio/experimental/concurrent_channel.hpp \
asio/experimental/coro.hpp \
asio/experimental/coro_traits.hpp \
asio/experimental/deferred.hpp \
+ asio/experimental/detail/channel_handler.hpp \
+ asio/experimental/detail/channel_message.hpp \
+ asio/experimental/detail/channel_operation.hpp \
+ asio/experimental/detail/channel_payload.hpp \
+ asio/experimental/detail/channel_receive_op.hpp \
+ asio/experimental/detail/channel_send_functions.hpp \
+ asio/experimental/detail/channel_send_op.hpp \
+ asio/experimental/detail/channel_service.hpp \
asio/experimental/detail/completion_handler_erasure.hpp \
asio/experimental/detail/coro_promise_allocator.hpp \
+ asio/experimental/detail/has_signature.hpp \
+ asio/experimental/detail/impl/channel_service.hpp \
asio/experimental/detail/partial_promise.hpp \
asio/experimental/impl/append.hpp \
asio/experimental/impl/as_single.hpp \
asio/experimental/impl/as_tuple.hpp \
+ asio/experimental/impl/channel_error.ipp \
asio/experimental/impl/coro.hpp \
asio/experimental/impl/deferred.hpp \
asio/experimental/impl/parallel_group.hpp \
diff --git a/asio/include/asio/detail/null_mutex.hpp b/asio/include/asio/detail/null_mutex.hpp
index b545733..1672ae5 100644
--- a/asio/include/asio/detail/null_mutex.hpp
+++ b/asio/include/asio/detail/null_mutex.hpp
@@ -17,8 +17,6 @@
#include "asio/detail/config.hpp"
-#if !defined(ASIO_HAS_THREADS)
-
#include "asio/detail/noncopyable.hpp"
#include "asio/detail/scoped_lock.hpp"
@@ -59,6 +57,4 @@
#include "asio/detail/pop_options.hpp"
-#endif // !defined(ASIO_HAS_THREADS)
-
#endif // ASIO_DETAIL_NULL_MUTEX_HPP
diff --git a/asio/include/asio/experimental/basic_channel.hpp b/asio/include/asio/experimental/basic_channel.hpp
new file mode 100644
index 0000000..e70b7e2
--- /dev/null
+++ b/asio/include/asio/experimental/basic_channel.hpp
@@ -0,0 +1,415 @@
+//
+// experimental/basic_channel.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_BASIC_CHANNEL_HPP
+#define ASIO_EXPERIMENTAL_BASIC_CHANNEL_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/non_const_lvalue.hpp"
+#include "asio/detail/null_mutex.hpp"
+#include "asio/execution/executor.hpp"
+#include "asio/execution_context.hpp"
+#include "asio/experimental/detail/channel_send_functions.hpp"
+#include "asio/experimental/detail/channel_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+} // namespace detail
+
+/// A channel for messages.
+template <typename Executor, typename Traits, typename... Signatures>
+class basic_channel
+#if !defined(GENERATING_DOCUMENTATION)
+ : public detail::channel_send_functions<
+ basic_channel<Executor, Traits, Signatures...>,
+ Executor, Signatures...>
+#endif // !defined(GENERATING_DOCUMENTATION)
+{
+private:
+ class initiate_async_send;
+ class initiate_async_receive;
+ typedef detail::channel_service<asio::detail::null_mutex> service_type;
+ typedef typename service_type::template implementation_type<
+ Traits, Signatures...>::payload_type payload_type;
+
+ template <typename... PayloadSignatures,
+ ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
+ auto do_async_receive(detail::channel_payload<PayloadSignatures...>*,
+ ASIO_MOVE_ARG(CompletionToken) token)
+ -> decltype(
+ async_initiate<CompletionToken, PayloadSignatures...>(
+ declval<initiate_async_receive>(), token))
+ {
+ return async_initiate<CompletionToken, PayloadSignatures...>(
+ initiate_async_receive(this), token);
+ }
+
+public:
+ /// The type of the executor associated with the channel.
+ typedef Executor executor_type;
+
+ /// Rebinds the channel type to another executor.
+ template <typename Executor1>
+ struct rebind_executor
+ {
+ /// The channel type when rebound to the specified executor.
+ typedef basic_channel<Executor1, Traits, Signatures...> other;
+ };
+
+ /// The traits type associated with the channel.
+ typedef typename Traits::template rebind<Signatures...>::other traits_type;
+
+ /// Construct a basic_channel.
+ /**
+ * This constructor creates and channel.
+ *
+ * @param ex The I/O executor that the channel will use, by default, to
+ * dispatch handlers for any asynchronous operations performed on the channel.
+ *
+ * @param max_buffer_size The maximum number of messages that may be buffered
+ * in the channel.
+ */
+ basic_channel(const executor_type& ex, std::size_t max_buffer_size = 0)
+ : service_(&asio::use_service<service_type>(
+ basic_channel::get_context(ex))),
+ impl_(),
+ executor_(ex)
+ {
+ service_->construct(impl_, max_buffer_size);
+ }
+
+ /// Construct and open a basic_channel.
+ /**
+ * This constructor creates and opens a channel.
+ *
+ * @param context An execution context which provides the I/O executor that
+ * the channel will use, by default, to dispatch handlers for any asynchronous
+ * operations performed on the channel.
+ *
+ * @param max_buffer_size The maximum number of messages that may be buffered
+ * in the channel.
+ */
+ template <typename ExecutionContext>
+ basic_channel(ExecutionContext& context, std::size_t max_buffer_size = 0,
+ typename constraint<
+ is_convertible<ExecutionContext&, execution_context&>::value,
+ defaulted_constraint
+ >::type = defaulted_constraint())
+ : service_(&asio::use_service<service_type>(context)),
+ impl_(),
+ executor_(context.get_executor())
+ {
+ service_->construct(impl_, max_buffer_size);
+ }
+
+#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+ /// Move-construct a basic_channel from another.
+ /**
+ * This constructor moves a channel from one object to another.
+ *
+ * @param other The other basic_channel object from which the move will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_channel(const executor_type&) constructor.
+ */
+ basic_channel(basic_channel&& other)
+ : service_(other.service_),
+ executor_(other.executor_)
+ {
+ service_->move_construct(impl_, other.impl_);
+ }
+
+ /// Move-assign a basic_channel from another.
+ /**
+ * This assignment operator moves a channel from one object to another.
+ * Cancels any outstanding asynchronous operations associated with the target
+ * object.
+ *
+ * @param other The other basic_channel object from which the move will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_channel(const executor_type&)
+ * constructor.
+ */
+ basic_channel& operator=(basic_channel&& other)
+ {
+ if (this != &other)
+ {
+ service_->move_assign(impl_, *other.service_, other.impl_);
+ executor_.~executor_type();
+ new (&executor_) executor_type(other.executor_);
+ service_ = other.service_;
+ }
+ return *this;
+ }
+
+ // All channels have access to each other's implementations.
+ template <typename, typename, typename...>
+ friend class basic_channel;
+
+ /// Move-construct a basic_channel from another.
+ /**
+ * This constructor moves a channel from one object to another.
+ *
+ * @param other The other basic_channel object from which the move will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_channel(const executor_type&)
+ * constructor.
+ */
+ template <typename Executor1>
+ basic_channel(
+ basic_channel<Executor1, Traits, Signatures...>&& other,
+ typename constraint<
+ is_convertible<Executor1, Executor>::value
+ >::type = 0)
+ : service_(other.service_),
+ executor_(other.executor_)
+ {
+ service_->move_construct(impl_, *other.service_, other.impl_);
+ }
+
+ /// Move-assign a basic_channel from another.
+ /**
+ * This assignment operator moves a channel from one object to another.
+ * Cancels any outstanding asynchronous operations associated with the target
+ * object.
+ *
+ * @param other The other basic_channel object from which the move will
+ * occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_channel(const executor_type&)
+ * constructor.
+ */
+ template <typename Executor1>
+ typename constraint<
+ is_convertible<Executor1, Executor>::value,
+ basic_channel&
+ >::type operator=(basic_channel<Executor1, Traits, Signatures...>&& other)
+ {
+ if (this != &other)
+ {
+ service_->move_assign(impl_, *other.service_, other.impl_);
+ executor_.~executor_type();
+ new (&executor_) executor_type(other.executor_);
+ service_ = other.service_;
+ }
+ return *this;
+ }
+#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+
+ /// Get the executor associated with the object.
+ executor_type get_executor() ASIO_NOEXCEPT
+ {
+ return executor_;
+ }
+
+ /// Get the capacity of the channel's buffer.
+ std::size_t capacity() ASIO_NOEXCEPT
+ {
+ return service_->capacity(impl_);
+ }
+
+ /// Determine whether the channel is open.
+ bool is_open() const ASIO_NOEXCEPT
+ {
+ return service_->is_open(impl_);
+ }
+
+ /// Reset the channel to its initial state.
+ void reset()
+ {
+ service_->reset(impl_);
+ }
+
+ /// Close the channel.
+ void close()
+ {
+ service_->close(impl_);
+ }
+
+ /// Cancel all asynchronous operations waiting on the channel.
+ /**
+ * All outstanding send operations will complete with the error
+ * @c asio::experimental::error::channel_canceld. Outstanding receive
+ * operations complete with the result as determined by the channel traits.
+ */
+ void cancel()
+ {
+ service_->cancel(impl_);
+ }
+
+ /// Determine whether a message can be received without blocking.
+ bool ready() const ASIO_NOEXCEPT
+ {
+ return service_->ready(impl_);
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+
+ /// Try to send a message without blocking.
+ /**
+ * Fails if the buffer is full and there are no waiting receive operations.
+ *
+ * @returns @c true on success, @c false on failure.
+ */
+ template <typename... Args>
+ bool try_send(ASIO_MOVE_ARG(Args)... args);
+
+ /// Try to send a number of messages without blocking.
+ /**
+ * @returns The number of messages that were sent.
+ */
+ template <typename... Args>
+ std::size_t try_send_n(std::size_t count, ASIO_MOVE_ARG(Args)... args);
+
+ /// Asynchronously send a message.
+ template <typename... Args,
+ ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
+ CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
+ auto async_send(ASIO_MOVE_ARG(Args)... args,
+ ASIO_MOVE_ARG(CompletionToken) token);
+
+#endif // defined(GENERATING_DOCUMENTATION)
+
+ /// Try to receive a message without blocking.
+ /**
+ * Fails if the buffer is full and there are no waiting receive operations.
+ *
+ * @returns @c true on success, @c false on failure.
+ */
+ template <typename Handler>
+ bool try_receive(ASIO_MOVE_ARG(Handler) handler)
+ {
+ return service_->try_receive(impl_, ASIO_MOVE_CAST(Handler)(handler));
+ }
+
+ /// Asynchronously receive a message.
+ template <typename CompletionToken
+ ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
+ auto async_receive(
+ ASIO_MOVE_ARG(CompletionToken) token
+ ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
+#if !defined(GENERATING_DOCUMENTATION)
+ -> decltype(
+ this->do_async_receive(static_cast<payload_type*>(0),
+ ASIO_MOVE_CAST(CompletionToken)(token)))
+#endif // !defined(GENERATING_DOCUMENTATION)
+ {
+ return this->do_async_receive(static_cast<payload_type*>(0),
+ ASIO_MOVE_CAST(CompletionToken)(token));
+ }
+
+private:
+ // Disallow copying and assignment.
+ basic_channel(const basic_channel&) ASIO_DELETED;
+ basic_channel& operator=(const basic_channel&) ASIO_DELETED;
+
+ template <typename, typename, typename...>
+ friend class detail::channel_send_functions;
+
+ // Helper function to get an executor's context.
+ template <typename T>
+ static execution_context& get_context(const T& t,
+ typename enable_if<execution::is_executor<T>::value>::type* = 0)
+ {
+ return asio::query(t, execution::context);
+ }
+
+ // Helper function to get an executor's context.
+ template <typename T>
+ static execution_context& get_context(const T& t,
+ typename enable_if<!execution::is_executor<T>::value>::type* = 0)
+ {
+ return t.context();
+ }
+
+ class initiate_async_send
+ {
+ public:
+ typedef Executor executor_type;
+
+ explicit initiate_async_send(basic_channel* self)
+ : self_(self)
+ {
+ }
+
+ executor_type get_executor() const ASIO_NOEXCEPT
+ {
+ return self_->get_executor();
+ }
+
+ template <typename SendHandler>
+ void operator()(ASIO_MOVE_ARG(SendHandler) handler,
+ ASIO_MOVE_ARG(payload_type) payload) const
+ {
+ asio::detail::non_const_lvalue<SendHandler> handler2(handler);
+ self_->service_->async_send(self_->impl_,
+ ASIO_MOVE_CAST(payload_type)(payload),
+ handler2.value, self_->get_executor());
+ }
+
+ private:
+ basic_channel* self_;
+ };
+
+ class initiate_async_receive
+ {
+ public:
+ typedef Executor executor_type;
+
+ explicit initiate_async_receive(basic_channel* self)
+ : self_(self)
+ {
+ }
+
+ executor_type get_executor() const ASIO_NOEXCEPT
+ {
+ return self_->get_executor();
+ }
+
+ template <typename ReceiveHandler>
+ void operator()(ASIO_MOVE_ARG(ReceiveHandler) handler) const
+ {
+ asio::detail::non_const_lvalue<ReceiveHandler> handler2(handler);
+ self_->service_->async_receive(self_->impl_,
+ handler2.value, self_->get_executor());
+ }
+
+ private:
+ basic_channel* self_;
+ };
+
+ // The service associated with the I/O object.
+ service_type* service_;
+
+ // The underlying implementation of the I/O object.
+ typename service_type::template implementation_type<
+ Traits, Signatures...> impl_;
+
+ // The associated executor.
+ Executor executor_;
+};
+
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_BASIC_CHANNEL_HPP
diff --git a/asio/include/asio/experimental/basic_concurrent_channel.hpp b/asio/include/asio/experimental/basic_concurrent_channel.hpp
new file mode 100644
index 0000000..5557a44
--- /dev/null
+++ b/asio/include/asio/experimental/basic_concurrent_channel.hpp
@@ -0,0 +1,424 @@
+//
+// experimental/basic_concurrent_channel.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_BASIC_CONCURRENT_CHANNEL_HPP
+#define ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_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/non_const_lvalue.hpp"
+#include "asio/detail/mutex.hpp"
+#include "asio/execution/executor.hpp"
+#include "asio/execution_context.hpp"
+#include "asio/experimental/detail/channel_send_functions.hpp"
+#include "asio/experimental/detail/channel_service.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+} // namespace detail
+
+/// A channel for messages.
+template <typename Executor, typename Traits, typename... Signatures>
+class basic_concurrent_channel
+#if !defined(GENERATING_DOCUMENTATION)
+ : public detail::channel_send_functions<
+ basic_concurrent_channel<Executor, Traits, Signatures...>,
+ Executor, Signatures...>
+#endif // !defined(GENERATING_DOCUMENTATION)
+{
+private:
+ class initiate_async_send;
+ class initiate_async_receive;
+ typedef detail::channel_service<asio::detail::mutex> service_type;
+ typedef typename service_type::template implementation_type<
+ Traits, Signatures...>::payload_type payload_type;
+
+ template <typename... PayloadSignatures,
+ ASIO_COMPLETION_TOKEN_FOR(PayloadSignatures...) CompletionToken>
+ auto do_async_receive(detail::channel_payload<PayloadSignatures...>*,
+ ASIO_MOVE_ARG(CompletionToken) token)
+ -> decltype(
+ async_initiate<CompletionToken, PayloadSignatures...>(
+ declval<initiate_async_receive>(), token))
+ {
+ return async_initiate<CompletionToken, PayloadSignatures...>(
+ initiate_async_receive(this), token);
+ }
+
+public:
+ /// The type of the executor associated with the channel.
+ typedef Executor executor_type;
+
+ /// Rebinds the channel type to another executor.
+ template <typename Executor1>
+ struct rebind_executor
+ {
+ /// The channel type when rebound to the specified executor.
+ typedef basic_concurrent_channel<Executor1, Traits, Signatures...> other;
+ };
+
+ /// The traits type associated with the channel.
+ typedef typename Traits::template rebind<Signatures...>::other traits_type;
+
+ /// Construct a basic_concurrent_channel.
+ /**
+ * This constructor creates and channel.
+ *
+ * @param ex The I/O executor that the channel will use, by default, to
+ * dispatch handlers for any asynchronous operations performed on the channel.
+ *
+ * @param max_buffer_size The maximum number of messages that may be buffered
+ * in the channel.
+ */
+ basic_concurrent_channel(const executor_type& ex,
+ std::size_t max_buffer_size = 0)
+ : service_(&asio::use_service<service_type>(
+ basic_concurrent_channel::get_context(ex))),
+ impl_(),
+ executor_(ex)
+ {
+ service_->construct(impl_, max_buffer_size);
+ }
+
+ /// Construct and open a basic_concurrent_channel.
+ /**
+ * This constructor creates and opens a channel.
+ *
+ * @param context An execution context which provides the I/O executor that
+ * the channel will use, by default, to dispatch handlers for any asynchronous
+ * operations performed on the channel.
+ *
+ * @param max_buffer_size The maximum number of messages that may be buffered
+ * in the channel.
+ */
+ template <typename ExecutionContext>
+ basic_concurrent_channel(ExecutionContext& context,
+ std::size_t max_buffer_size = 0,
+ typename constraint<
+ is_convertible<ExecutionContext&, execution_context&>::value,
+ defaulted_constraint
+ >::type = defaulted_constraint())
+ : service_(&asio::use_service<service_type>(context)),
+ impl_(),
+ executor_(context.get_executor())
+ {
+ service_->construct(impl_, max_buffer_size);
+ }
+
+#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+ /// Move-construct a basic_concurrent_channel from another.
+ /**
+ * This constructor moves a channel from one object to another.
+ *
+ * @param other The other basic_concurrent_channel object from which the move
+ * will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_concurrent_channel(const executor_type&)
+ * constructor.
+ */
+ basic_concurrent_channel(basic_concurrent_channel&& other)
+ : service_(other.service_),
+ executor_(other.executor_)
+ {
+ service_->move_construct(impl_, other.impl_);
+ }
+
+ /// Move-assign a basic_concurrent_channel from another.
+ /**
+ * This assignment operator moves a channel from one object to another.
+ * Cancels any outstanding asynchronous operations associated with the target
+ * object.
+ *
+ * @param other The other basic_concurrent_channel object from which the move
+ * will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_concurrent_channel(const executor_type&)
+ * constructor.
+ */
+ basic_concurrent_channel& operator=(basic_concurrent_channel&& other)
+ {
+ if (this != &other)
+ {
+ service_->move_assign(impl_, *other.service_, other.impl_);
+ executor_.~executor_type();
+ new (&executor_) executor_type(other.executor_);
+ service_ = other.service_;
+ }
+ return *this;
+ }
+
+ // All channels have access to each other's implementations.
+ template <typename, typename, typename...>
+ friend class basic_concurrent_channel;
+
+ /// Move-construct a basic_concurrent_channel from another.
+ /**
+ * This constructor moves a channel from one object to another.
+ *
+ * @param other The other basic_concurrent_channel object from which the move
+ * will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_concurrent_channel(const executor_type&)
+ * constructor.
+ */
+ template <typename Executor1>
+ basic_concurrent_channel(
+ basic_concurrent_channel<Executor1, Traits, Signatures...>&& other,
+ typename constraint<
+ is_convertible<Executor1, Executor>::value
+ >::type = 0)
+ : service_(other.service_),
+ executor_(other.executor_)
+ {
+ service_->move_construct(impl_, *other.service_, other.impl_);
+ }
+
+ /// Move-assign a basic_concurrent_channel from another.
+ /**
+ * This assignment operator moves a channel from one object to another.
+ * Cancels any outstanding asynchronous operations associated with the target
+ * object.
+ *
+ * @param other The other basic_concurrent_channel object from which the move
+ * will occur.
+ *
+ * @note Following the move, the moved-from object is in the same state as if
+ * constructed using the @c basic_concurrent_channel(const executor_type&)
+ * constructor.
+ */
+ template <typename Executor1>
+ typename constraint<
+ is_convertible<Executor1, Executor>::value,
+ basic_concurrent_channel&
+ >::type operator=(
+ basic_concurrent_channel<Executor1, Traits, Signatures...>&& other)
+ {
+ if (this != &other)
+ {
+ service_->move_assign(impl_, *other.service_, other.impl_);
+ executor_.~executor_type();
+ new (&executor_) executor_type(other.executor_);
+ service_ = other.service_;
+ }
+ return *this;
+ }
+#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
+
+ /// Get the executor associated with the object.
+ executor_type get_executor() ASIO_NOEXCEPT
+ {
+ return executor_;
+ }
+
+ /// Get the capacity of the channel's buffer.
+ std::size_t capacity() ASIO_NOEXCEPT
+ {
+ return service_->capacity(impl_);
+ }
+
+ /// Determine whether the channel is open.
+ bool is_open() const ASIO_NOEXCEPT
+ {
+ return service_->is_open(impl_);
+ }
+
+ /// Reset the channel to its initial state.
+ void reset()
+ {
+ service_->reset(impl_);
+ }
+
+ /// Close the channel.
+ void close()
+ {
+ service_->close(impl_);
+ }
+
+ /// Cancel all asynchronous operations waiting on the channel.
+ /**
+ * All outstanding send operations will complete with the error
+ * @c asio::experimental::error::channel_canceld. Outstanding receive
+ * operations complete with the result as determined by the channel traits.
+ */
+ void cancel()
+ {
+ service_->cancel(impl_);
+ }
+
+ /// Determine whether a message can be received without blocking.
+ bool ready() const ASIO_NOEXCEPT
+ {
+ return service_->ready(impl_);
+ }
+
+#if defined(GENERATING_DOCUMENTATION)
+
+ /// Try to send a message without blocking.
+ /**
+ * Fails if the buffer is full and there are no waiting receive operations.
+ *
+ * @returns @c true on success, @c false on failure.
+ */
+ template <typename... Args>
+ bool try_send(ASIO_MOVE_ARG(Args)... args);
+
+ /// Try to send a number of messages without blocking.
+ /**
+ * @returns The number of messages that were sent.
+ */
+ template <typename... Args>
+ std::size_t try_send_n(std::size_t count, ASIO_MOVE_ARG(Args)... args);
+
+ /// Asynchronously send a message.
+ template <typename... Args,
+ ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
+ CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
+ auto async_send(ASIO_MOVE_ARG(Args)... args,
+ ASIO_MOVE_ARG(CompletionToken) token);
+
+#endif // defined(GENERATING_DOCUMENTATION)
+
+ /// Try to receive a message without blocking.
+ /**
+ * Fails if the buffer is full and there are no waiting receive operations.
+ *
+ * @returns @c true on success, @c false on failure.
+ */
+ template <typename Handler>
+ bool try_receive(ASIO_MOVE_ARG(Handler) handler)
+ {
+ return service_->try_receive(impl_, ASIO_MOVE_CAST(Handler)(handler));
+ }
+
+ /// Asynchronously receive a message.
+ template <typename CompletionToken
+ ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
+ auto async_receive(
+ ASIO_MOVE_ARG(CompletionToken) token
+ ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
+#if !defined(GENERATING_DOCUMENTATION)
+ -> decltype(
+ this->do_async_receive(static_cast<payload_type*>(0),
+ ASIO_MOVE_CAST(CompletionToken)(token)))
+#endif // !defined(GENERATING_DOCUMENTATION)
+ {
+ return this->do_async_receive(static_cast<payload_type*>(0),
+ ASIO_MOVE_CAST(CompletionToken)(token));
+ }
+
+private:
+ // Disallow copying and assignment.
+ basic_concurrent_channel(
+ const basic_concurrent_channel&) ASIO_DELETED;
+ basic_concurrent_channel& operator=(
+ const basic_concurrent_channel&) ASIO_DELETED;
+
+ template <typename, typename, typename...>
+ friend class detail::channel_send_functions;
+
+ // Helper function to get an executor's context.
+ template <typename T>
+ static execution_context& get_context(const T& t,
+ typename enable_if<execution::is_executor<T>::value>::type* = 0)
+ {
+ return asio::query(t, execution::context);
+ }
+
+ // Helper function to get an executor's context.
+ template <typename T>
+ static execution_context& get_context(const T& t,
+ typename enable_if<!execution::is_executor<T>::value>::type* = 0)
+ {
+ return t.context();
+ }
+
+ class initiate_async_send
+ {
+ public:
+ typedef Executor executor_type;
+
+ explicit initiate_async_send(basic_concurrent_channel* self)
+ : self_(self)
+ {
+ }
+
+ executor_type get_executor() const ASIO_NOEXCEPT
+ {
+ return self_->get_executor();
+ }
+
+ template <typename SendHandler>
+ void operator()(ASIO_MOVE_ARG(SendHandler) handler,
+ ASIO_MOVE_ARG(payload_type) payload) const
+ {
+ asio::detail::non_const_lvalue<SendHandler> handler2(handler);
+ self_->service_->async_send(self_->impl_,
+ ASIO_MOVE_CAST(payload_type)(payload),
+ handler2.value, self_->get_executor());
+ }
+
+ private:
+ basic_concurrent_channel* self_;
+ };
+
+ class initiate_async_receive
+ {
+ public:
+ typedef Executor executor_type;
+
+ explicit initiate_async_receive(basic_concurrent_channel* self)
+ : self_(self)
+ {
+ }
+
+ executor_type get_executor() const ASIO_NOEXCEPT
+ {
+ return self_->get_executor();
+ }
+
+ template <typename ReceiveHandler>
+ void operator()(ASIO_MOVE_ARG(ReceiveHandler) handler) const
+ {
+ asio::detail::non_const_lvalue<ReceiveHandler> handler2(handler);
+ self_->service_->async_receive(self_->impl_,
+ handler2.value, self_->get_executor());
+ }
+
+ private:
+ basic_concurrent_channel* self_;
+ };
+
+ // The service associated with the I/O object.
+ service_type* service_;
+
+ // The underlying implementation of the I/O object.
+ typename service_type::template implementation_type<
+ Traits, Signatures...> impl_;
+
+ // The associated executor.
+ Executor executor_;
+};
+
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_BASIC_CONCURRENT_CHANNEL_HPP
diff --git a/asio/include/asio/experimental/channel.hpp b/asio/include/asio/experimental/channel.hpp
new file mode 100644
index 0000000..3dc6a8b
--- /dev/null
+++ b/asio/include/asio/experimental/channel.hpp
@@ -0,0 +1,70 @@
+//
+// experimental/channel.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_CHANNEL_HPP
+#define ASIO_EXPERIMENTAL_CHANNEL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/any_io_executor.hpp"
+#include "asio/detail/type_traits.hpp"
+#include "asio/execution/executor.hpp"
+#include "asio/is_executor.hpp"
+#include "asio/experimental/basic_channel.hpp"
+#include "asio/experimental/channel_traits.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename ExecutorOrSignature, typename = void>
+struct channel_type
+{
+ template <typename... Signatures>
+ struct inner
+ {
+ typedef basic_channel<any_io_executor, channel_traits<>,
+ ExecutorOrSignature, Signatures...> type;
+ };
+};
+
+template <typename ExecutorOrSignature>
+struct channel_type<ExecutorOrSignature,
+ typename enable_if<
+ is_executor<ExecutorOrSignature>::value
+ || execution::is_executor<ExecutorOrSignature>::value
+ >::type>
+{
+ template <typename... Signatures>
+ struct inner
+ {
+ typedef basic_channel<ExecutorOrSignature,
+ channel_traits<>, Signatures...> type;
+ };
+};
+
+} // namespace detail
+
+/// Template type alias for common use of channel.
+template <typename ExecutorOrSignature, typename... Signatures>
+using channel = typename detail::channel_type<
+ ExecutorOrSignature>::template inner<Signatures...>::type;
+
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_CHANNEL_HPP
diff --git a/asio/include/asio/experimental/channel_error.hpp b/asio/include/asio/experimental/channel_error.hpp
new file mode 100644
index 0000000..1b3e4f4
--- /dev/null
+++ b/asio/include/asio/experimental/channel_error.hpp
@@ -0,0 +1,84 @@
+//
+// experimental/channel_error.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_CHANNEL_ERROR_HPP
+#define ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/error_code.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace error {
+
+enum channel_errors
+{
+ /// The channel was closed.
+ channel_closed = 1,
+
+ /// The channel was cancelled.
+ channel_cancelled = 2
+};
+
+extern ASIO_DECL
+const asio::error_category& get_channel_category();
+
+static const asio::error_category&
+ channel_category ASIO_UNUSED_VARIABLE
+ = asio::experimental::error::get_channel_category();
+
+} // namespace error
+namespace channel_errc {
+ // Simulates a scoped enum.
+ using error::channel_closed;
+ using error::channel_cancelled;
+} // namespace channel_errc
+} // namespace experimental
+} // namespace asio
+
+#if defined(ASIO_HAS_STD_SYSTEM_ERROR)
+namespace std {
+
+template<> struct is_error_code_enum<
+ asio::experimental::error::channel_errors>
+{
+ static const bool value = true;
+};
+
+} // namespace std
+#endif // defined(ASIO_HAS_STD_SYSTEM_ERROR)
+
+namespace asio {
+namespace experimental {
+namespace error {
+
+inline asio::error_code make_error_code(channel_errors e)
+{
+ return asio::error_code(
+ static_cast<int>(e), get_channel_category());
+}
+
+} // namespace error
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#if defined(ASIO_HEADER_ONLY)
+# include "asio/experimental/impl/channel_error.ipp"
+#endif // defined(ASIO_HEADER_ONLY)
+
+#endif // ASIO_EXPERIMENTAL_CHANNEL_ERROR_HPP
diff --git a/asio/include/asio/experimental/channel_traits.hpp b/asio/include/asio/experimental/channel_traits.hpp
new file mode 100644
index 0000000..a436e50
--- /dev/null
+++ b/asio/include/asio/experimental/channel_traits.hpp
@@ -0,0 +1,231 @@
+//
+// experimental/channel_traits.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_CHANNEL_TRAITS_HPP
+#define ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <deque>
+#include "asio/detail/type_traits.hpp"
+#include "asio/error.hpp"
+#include "asio/experimental/channel_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+
+#if defined(GENERATING_DOCUMENTATION)
+
+template <typename... Signatures>
+struct channel_traits
+{
+ /// Rebind the traits to a new set of signatures.
+ /**
+ * This nested structure must have a single nested type @c other that
+ * aliases a traits type with the specified set of signatures.
+ */
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef user_defined other;
+ };
+
+ /// Determine the container for the specified elements.
+ /**
+ * This nested structure must have a single nested type @c other that
+ * aliases a container type for the specified element type.
+ */
+ template <typename Element>
+ struct container
+ {
+ typedef user_defined type;
+ };
+
+ /// The signature of a channel cancellation notification.
+ typedef void receive_cancelled_signature(...);
+
+ /// Invoke the specified handler with a cancellation notification.
+ template <typename F>
+ static void invoke_receive_cancelled(F f);
+
+ /// The signature of a channel closed notification.
+ typedef void receive_closed_signature(...);
+
+ /// Invoke the specified handler with a closed notification.
+ template <typename F>
+ static void invoke_receive_closed(F f);
+};
+
+#else // defined(GENERATING_DOCUMENTATION)
+
+/// Traits used for customising channel behaviour.
+template <typename... Signatures>
+struct channel_traits
+{
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef channel_traits<NewSignatures...> other;
+ };
+};
+
+template <typename R, typename... Args, typename... Signatures>
+struct channel_traits<R(asio::error_code, Args...), Signatures...>
+{
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef channel_traits<NewSignatures...> other;
+ };
+
+ template <typename Element>
+ struct container
+ {
+ typedef std::deque<Element> type;
+ };
+
+ typedef R receive_cancelled_signature(asio::error_code, Args...);
+
+ template <typename F>
+ static void invoke_receive_cancelled(F f)
+ {
+ const asio::error_code e = error::channel_cancelled;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e, typename decay<Args>::type()...);
+ }
+
+ typedef R receive_closed_signature(asio::error_code, Args...);
+
+ template <typename F>
+ static void invoke_receive_closed(F f)
+ {
+ const asio::error_code e = error::channel_closed;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e, typename decay<Args>::type()...);
+ }
+};
+
+template <typename R, typename... Args, typename... Signatures>
+struct channel_traits<R(std::exception_ptr, Args...), Signatures...>
+{
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef channel_traits<NewSignatures...> other;
+ };
+
+ template <typename Element>
+ struct container
+ {
+ typedef std::deque<Element> type;
+ };
+
+ typedef R receive_cancelled_signature(std::exception_ptr, Args...);
+
+ template <typename F>
+ static void invoke_receive_cancelled(F f)
+ {
+ const asio::error_code e = error::channel_cancelled;
+ ASIO_MOVE_OR_LVALUE(F)(f)(
+ std::make_exception_ptr(asio::system_error(e)),
+ typename decay<Args>::type()...);
+ }
+
+ typedef R receive_closed_signature(std::exception_ptr, Args...);
+
+ template <typename F>
+ static void invoke_receive_closed(F f)
+ {
+ const asio::error_code e = error::channel_closed;
+ ASIO_MOVE_OR_LVALUE(F)(f)(
+ std::make_exception_ptr(asio::system_error(e)),
+ typename decay<Args>::type()...);
+ }
+};
+
+template <typename R>
+struct channel_traits<R()>
+{
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef channel_traits<NewSignatures...> other;
+ };
+
+ template <typename Element>
+ struct container
+ {
+ typedef std::deque<Element> type;
+ };
+
+ typedef R receive_cancelled_signature(asio::error_code);
+
+ template <typename F>
+ static void invoke_receive_cancelled(F f)
+ {
+ const asio::error_code e = error::channel_cancelled;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e);
+ }
+
+ typedef R receive_closed_signature(asio::error_code);
+
+ template <typename F>
+ static void invoke_receive_closed(F f)
+ {
+ const asio::error_code e = error::channel_closed;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e);
+ }
+};
+
+template <typename R, typename T>
+struct channel_traits<R(T)>
+{
+ template <typename... NewSignatures>
+ struct rebind
+ {
+ typedef channel_traits<NewSignatures...> other;
+ };
+
+ template <typename Element>
+ struct container
+ {
+ typedef std::deque<Element> type;
+ };
+
+ typedef R receive_cancelled_signature(asio::error_code);
+
+ template <typename F>
+ static void invoke_receive_cancelled(F f)
+ {
+ const asio::error_code e = error::channel_cancelled;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e);
+ }
+
+ typedef R receive_closed_signature(asio::error_code);
+
+ template <typename F>
+ static void invoke_receive_closed(F f)
+ {
+ const asio::error_code e = error::channel_closed;
+ ASIO_MOVE_OR_LVALUE(F)(f)(e);
+ }
+};
+
+#endif // defined(GENERATING_DOCUMENTATION)
+
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_CHANNEL_TRAITS_HPP
diff --git a/asio/include/asio/experimental/concurrent_channel.hpp b/asio/include/asio/experimental/concurrent_channel.hpp
new file mode 100644
index 0000000..0519b4e
--- /dev/null
+++ b/asio/include/asio/experimental/concurrent_channel.hpp
@@ -0,0 +1,70 @@
+//
+// experimental/concurrent_channel.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_CONCURRENT_CHANNEL_HPP
+#define ASIO_EXPERIMENTAL_CONCURRENT_CHANNEL_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/any_io_executor.hpp"
+#include "asio/detail/type_traits.hpp"
+#include "asio/execution/executor.hpp"
+#include "asio/is_executor.hpp"
+#include "asio/experimental/basic_concurrent_channel.hpp"
+#include "asio/experimental/channel_traits.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename ExecutorOrSignature, typename = void>
+struct concurrent_channel_type
+{
+ template <typename... Signatures>
+ struct inner
+ {
+ typedef basic_concurrent_channel<any_io_executor, channel_traits<>,
+ ExecutorOrSignature, Signatures...> type;
+ };
+};
+
+template <typename ExecutorOrSignature>
+struct concurrent_channel_type<ExecutorOrSignature,
+ typename enable_if<
+ is_executor<ExecutorOrSignature>::value
+ || execution::is_executor<ExecutorOrSignature>::value
+ >::type>
+{
+ template <typename... Signatures>
+ struct inner
+ {
+ typedef basic_concurrent_channel<ExecutorOrSignature,
+ channel_traits<>, Signatures...> type;
+ };
+};
+
+} // namespace detail
+
+/// Template type alias for common use of channel.
+template <typename ExecutorOrSignature, typename... Signatures>
+using concurrent_channel = typename detail::concurrent_channel_type<
+ ExecutorOrSignature>::template inner<Signatures...>::type;
+
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_CONCURRENT_CHANNEL_HPP
diff --git a/asio/include/asio/experimental/detail/channel_handler.hpp b/asio/include/asio/experimental/detail/channel_handler.hpp
new file mode 100644
index 0000000..04e3700
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_handler.hpp
@@ -0,0 +1,70 @@
+//
+// experimental/detail/channel_handler.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_HANDLER_HPP
+#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_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/associator.hpp"
+#include "asio/experimental/detail/channel_payload.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename Payload, typename Handler>
+class channel_handler
+{
+public:
+ channel_handler(ASIO_MOVE_ARG(Payload) p, Handler& h)
+ : payload_(ASIO_MOVE_CAST(Payload)(p)),
+ handler_(ASIO_MOVE_CAST(Handler)(h))
+ {
+ }
+
+ void operator()()
+ {
+ payload_.receive(handler_);
+ }
+
+//private:
+ Payload payload_;
+ Handler handler_;
+};
+
+} // namespace detail
+} // namespace experimental
+
+template <template <typename, typename> class Associator,
+ typename Payload, typename Handler, typename DefaultCandidate>
+struct associator<Associator,
+ experimental::detail::channel_handler<Payload, Handler>,
+ DefaultCandidate>
+ : Associator<Handler, DefaultCandidate>
+{
+ static typename Associator<Handler, DefaultCandidate>::type get(
+ const experimental::detail::channel_handler<Payload, Handler>& h,
+ const DefaultCandidate& c = DefaultCandidate()) ASIO_NOEXCEPT
+ {
+ return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
+ }
+};
+
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_HANDLER_HPP
diff --git a/asio/include/asio/experimental/detail/channel_message.hpp b/asio/include/asio/experimental/detail/channel_message.hpp
new file mode 100644
index 0000000..4c4183d
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_message.hpp
@@ -0,0 +1,122 @@
+//
+// experimental/detail/channel_message.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_MESSAGE_HPP
+#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_MESSAGE_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <tuple>
+#include "asio/detail/type_traits.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename Signature>
+class channel_message;
+
+template <typename R>
+class channel_message<R()>
+{
+public:
+ channel_message(int)
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ ASIO_MOVE_OR_LVALUE(Handler)(handler)();
+ }
+};
+
+template <typename R, typename Arg0>
+class channel_message<R(Arg0)>
+{
+public:
+ template <typename T0>
+ channel_message(int, ASIO_MOVE_ARG(T0) t0)
+ : arg0_(ASIO_MOVE_CAST(T0)(t0))
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ ASIO_MOVE_OR_LVALUE(Handler)(handler)(
+ ASIO_MOVE_CAST(arg0_type)(arg0_));
+ }
+
+private:
+ typedef typename decay<Arg0>::type arg0_type;
+ arg0_type arg0_;
+};
+
+template <typename R, typename Arg0, typename Arg1>
+class channel_message<R(Arg0, Arg1)>
+{
+public:
+ template <typename T0, typename T1>
+ channel_message(int, ASIO_MOVE_ARG(T0) t0, ASIO_MOVE_ARG(T1) t1)
+ : arg0_(ASIO_MOVE_CAST(T0)(t0)),
+ arg1_(ASIO_MOVE_CAST(T1)(t1))
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ ASIO_MOVE_OR_LVALUE(Handler)(handler)(
+ ASIO_MOVE_CAST(arg0_type)(arg0_),
+ ASIO_MOVE_CAST(arg1_type)(arg1_));
+ }
+
+private:
+ typedef typename decay<Arg0>::type arg0_type;
+ arg0_type arg0_;
+ typedef typename decay<Arg1>::type arg1_type;
+ arg1_type arg1_;
+};
+
+template <typename R, typename... Args>
+class channel_message<R(Args...)>
+{
+public:
+ template <typename... T>
+ channel_message(int, ASIO_MOVE_ARG(T)... t)
+ : args_(ASIO_MOVE_CAST(T)(t)...)
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& h)
+ {
+ std::apply(ASIO_MOVE_OR_LVALUE(Handler)(h),
+ ASIO_MOVE_CAST(args_type)(args_));
+ }
+
+private:
+ typedef std::tuple<typename decay<Args>::type...> args_type;
+ args_type args_;
+};
+
+} // namespace detail
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_MESSAGE_HPP
diff --git a/asio/include/asio/experimental/detail/channel_operation.hpp b/asio/include/asio/experimental/detail/channel_operation.hpp
new file mode 100644
index 0000000..a1ff1ef
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_operation.hpp
@@ -0,0 +1,199 @@
+//
+// experimental/detail/channel_operation.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/detail/op_queue.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>
+ 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,
+ complete_op = 1,
+ cancel_op = 2,
+ close_op = 3
+ };
+
+ 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>
+class channel_operation::handler_work_base
+{
+public:
+ handler_work_base(int, const Executor& ex)
+ : executor_(asio::prefer(ex, execution::outstanding_work.tracked))
+ {
+ }
+
+ template <typename Function, typename Handler>
+ void post(Function& function, Handler& handler)
+ {
+ typename associated_allocator<Handler>::type allocator =
+ (get_associated_allocator)(handler);
+
+ execution::execute(
+ asio::prefer(
+ asio::require(executor_, execution::blocking.never),
+ execution::allocator(allocator)),
+ ASIO_MOVE_CAST(Function)(function));
+ }
+
+private:
+ typename decay<
+ typename prefer_result<Executor,
+ execution::outstanding_work_t::tracked_t
+ >::type
+ >::type executor_;
+};
+
+#if !defined(ASIO_NO_TS_EXECUTORS)
+
+template <typename Executor>
+class channel_operation::handler_work_base<Executor,
+ typename enable_if<
+ !execution::is_executor<Executor>::value
+ >::type>
+{
+public:
+ handler_work_base(int, const Executor& ex)
+ : work_(ex)
+ {
+ }
+
+ template <typename Function, typename Handler>
+ void post(Function& function, Handler& handler)
+ {
+ typename associated_allocator<Handler>::type allocator =
+ (get_associated_allocator)(handler);
+
+ work_.get_executor().post(
+ ASIO_MOVE_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<
+ typename associated_executor<Handler, IoExecutor>::type, IoExecutor>
+{
+public:
+ typedef channel_operation::handler_work_base<IoExecutor> base1_type;
+
+ typedef channel_operation::handler_work_base<
+ typename associated_executor<Handler, IoExecutor>::type, IoExecutor>
+ base2_type;
+
+ handler_work(Handler& handler, const IoExecutor& io_ex) ASIO_NOEXCEPT
+ : base1_type(0, io_ex),
+ base2_type(0, (get_associated_executor)(handler, io_ex))
+ {
+ }
+
+ template <typename Function>
+ void complete(Function& function, Handler& handler)
+ {
+ base2_type::post(function, handler);
+ }
+};
+
+template <typename Handler, typename IoExecutor>
+class channel_operation::handler_work<
+ Handler, IoExecutor,
+ typename enable_if<
+ is_same<
+ typename associated_executor<Handler,
+ IoExecutor>::asio_associated_executor_is_unspecialised,
+ void
+ >::value
+ >::type> : handler_work_base<IoExecutor>
+{
+public:
+ typedef channel_operation::handler_work_base<IoExecutor> base1_type;
+
+ handler_work(Handler&, const IoExecutor& io_ex) ASIO_NOEXCEPT
+ : base1_type(0, io_ex)
+ {
+ }
+
+ template <typename Function>
+ void complete(Function& function, Handler& handler)
+ {
+ base1_type::post(function, handler);
+ }
+};
+
+} // namespace detail
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_OPERATION_HPP
diff --git a/asio/include/asio/experimental/detail/channel_payload.hpp b/asio/include/asio/experimental/detail/channel_payload.hpp
new file mode 100644
index 0000000..eaf7598
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_payload.hpp
@@ -0,0 +1,93 @@
+//
+// experimental/detail/channel_payload.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_PAYLOAD_HPP
+#define ASIO_EXPERIMENTAL_DETAIL_CHANNEL_PAYLOAD_HPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include <variant>
+#include "asio/detail/type_traits.hpp"
+#include "asio/experimental/detail/channel_message.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename... Signatures>
+class channel_payload
+{
+public:
+ template <typename Signature>
+ channel_payload(ASIO_MOVE_ARG(channel_message<Signature>) m)
+ : message_(ASIO_MOVE_CAST(channel_message<Signature>)(m))
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ std::visit(
+ [&](auto& message)
+ {
+ message.receive(handler);
+ }, message_);
+ }
+
+private:
+ std::variant<channel_message<Signatures>...> message_;
+};
+
+template <typename R>
+class channel_payload<R()>
+{
+public:
+ explicit channel_payload(channel_message<R()>)
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ ASIO_MOVE_OR_LVALUE(Handler)(handler)();
+ }
+};
+
+template <typename Signature>
+class channel_payload<Signature>
+{
+public:
+ channel_payload(ASIO_MOVE_ARG(channel_message<Signature>) m)
+ : message_(ASIO_MOVE_CAST(channel_message<Signature>)(m))
+ {
+ }
+
+ template <typename Handler>
+ void receive(Handler& handler)
+ {
+ message_.receive(handler);
+ }
+
+private:
+ channel_message<Signature> message_;
+};
+
+} // namespace detail
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_PAYLOAD_HPP
diff --git a/asio/include/asio/experimental/detail/channel_receive_op.hpp b/asio/include/asio/experimental/detail/channel_receive_op.hpp
new file mode 100644
index 0000000..233a145
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_receive_op.hpp
@@ -0,0 +1,112 @@
+//
+// experimental/detail/channel_receive_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/handler_alloc_helpers.hpp"
+#include "asio/error.hpp"
+#include "asio/experimental/detail/channel_handler.hpp"
+#include "asio/experimental/detail/channel_operation.hpp"
+#include "asio/experimental/detail/channel_payload.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace detail {
+
+template <typename Payload>
+class channel_receive : public channel_operation
+{
+public:
+ void complete(Payload payload)
+ {
+ func_(this, complete_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_(ASIO_MOVE_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(
+ ASIO_MOVE_CAST2(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::complete_op)
+ {
+ Payload* payload = static_cast<Payload*>(v);
+ channel_handler<Payload, Handler> handler(
+ ASIO_MOVE_CAST(Payload)(*payload), o->handler_);
+ p.h = asio::detail::addressof(handler.handler_);
+ p.reset();
+ ASIO_HANDLER_INVOCATION_BEGIN(());
+ w.complete(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
diff --git a/asio/include/asio/experimental/detail/channel_send_functions.hpp b/asio/include/asio/experimental/detail/channel_send_functions.hpp
new file mode 100644
index 0000000..878e58c
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_send_functions.hpp
@@ -0,0 +1,131 @@
+//
+// experimental/detail/channel_send_functions.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/type_traits.hpp"
+#include "asio/experimental/detail/channel_message.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>
+ typename enable_if<
+ is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
+ bool
+ >::type try_send(ASIO_MOVE_ARG(Args2)... args)
+ {
+ typedef typename detail::channel_message<R(Args...)> message_type;
+ Derived* self = static_cast<Derived*>(this);
+ return self->service_->template try_send<message_type>(
+ self->impl_, ASIO_MOVE_CAST(Args2)(args)...);
+ }
+
+ template <typename... Args2>
+ typename enable_if<
+ is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
+ std::size_t
+ >::type try_send_n(std::size_t count, ASIO_MOVE_ARG(Args2)... args)
+ {
+ typedef typename detail::channel_message<R(Args...)> message_type;
+ Derived* self = static_cast<Derived*>(this);
+ return self->service_->template try_send_n<message_type>(
+ self->impl_, count, ASIO_MOVE_CAST(Args2)(args)...);
+ }
+
+ template <
+ ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
+ CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
+ auto async_send(Args... args,
+ ASIO_MOVE_ARG(CompletionToken) token
+ ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
+ {
+ typedef typename Derived::payload_type payload_type;
+ typedef typename detail::channel_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, ASIO_MOVE_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>
+ typename enable_if<
+ is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
+ bool
+ >::type try_send(ASIO_MOVE_ARG(Args2)... args)
+ {
+ typedef typename detail::channel_message<R(Args...)> message_type;
+ Derived* self = static_cast<Derived*>(this);
+ return self->service_->template try_send<message_type>(
+ self->impl_, ASIO_MOVE_CAST(Args2)(args)...);
+ }
+
+ template <typename... Args2>
+ typename enable_if<
+ is_constructible<detail::channel_message<R(Args...)>, int, Args2...>::value,
+ std::size_t
+ >::type try_send_n(std::size_t count, ASIO_MOVE_ARG(Args2)... args)
+ {
+ typedef typename detail::channel_message<R(Args...)> message_type;
+ Derived* self = static_cast<Derived*>(this);
+ return self->service_->template try_send_n<message_type>(
+ self->impl_, count, ASIO_MOVE_CAST(Args2)(args)...);
+ }
+
+ template <
+ ASIO_COMPLETION_TOKEN_FOR(void (asio::error_code))
+ CompletionToken ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)>
+ auto async_send(Args... args,
+ ASIO_MOVE_ARG(CompletionToken) token
+ ASIO_DEFAULT_COMPLETION_TOKEN(Executor))
+ {
+ typedef typename Derived::payload_type payload_type;
+ typedef typename detail::channel_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, ASIO_MOVE_CAST(Args)(args)...)));
+ }
+};
+
+} // namespace detail
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SEND_FUNCTIONS_HPP
diff --git a/asio/include/asio/experimental/detail/channel_send_op.hpp b/asio/include/asio/experimental/detail/channel_send_op.hpp
new file mode 100644
index 0000000..027671f
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_send_op.hpp
@@ -0,0 +1,140 @@
+//
+// experimental/detail/channel_send_op.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/experimental/detail/channel_payload.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 ASIO_MOVE_CAST(Payload)(payload_);
+ }
+
+ void complete()
+ {
+ func_(this, complete_op, 0);
+ }
+
+ void cancel()
+ {
+ func_(this, cancel_op, 0);
+ }
+
+ void close()
+ {
+ func_(this, close_op, 0);
+ }
+
+protected:
+ channel_send(func_type func, ASIO_MOVE_ARG(Payload) payload)
+ : channel_operation(func),
+ payload_(ASIO_MOVE_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(ASIO_MOVE_ARG(Payload) payload,
+ Handler& handler, const IoExecutor& io_ex)
+ : channel_send<Payload>(&channel_send_op::do_action,
+ ASIO_MOVE_CAST(Payload)(payload)),
+ handler_(ASIO_MOVE_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(
+ ASIO_MOVE_CAST2(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_));
+ w.complete(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
diff --git a/asio/include/asio/experimental/detail/channel_service.hpp b/asio/include/asio/experimental/detail/channel_service.hpp
new file mode 100644
index 0000000..98d9724
--- /dev/null
+++ b/asio/include/asio/experimental/detail/channel_service.hpp
@@ -0,0 +1,497 @@
+//
+// experimental/detail/channel_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/mutex.hpp"
+#include "asio/detail/op_queue.hpp"
+#include "asio/execution_context.hpp"
+#include "asio/experimental/detail/channel_message.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(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 ASIO_NOEXCEPT;
+
+ // Determine whether the channel is open.
+ bool is_open(const base_implementation_type& impl) const ASIO_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 ASIO_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,
+ ASIO_MOVE_ARG(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, ASIO_MOVE_ARG(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,
+ ASIO_MOVE_ARG2(typename implementation_type<
+ Traits, Signatures...>::payload_type) payload,
+ Handler& handler, const IoExecutor& io_ex)
+ {
+ typename associated_cancellation_slot<Handler>::type 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(ASIO_MOVE_CAST2(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,
+ ASIO_MOVE_ARG(Handler) handler);
+
+ // Asynchronously send a new value into the channel.
+ // 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)
+ {
+ typename associated_cancellation_slot<Handler>::type 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 complete_receive
+ {
+ explicit complete_receive(channel_receive<Payload>* op)
+ : op_(op)
+ {
+ }
+
+ template <typename... Args>
+ void operator()(ASIO_MOVE_ARG(Args)... args)
+ {
+ op_->complete(
+ channel_message<Signature>(0,
+ ASIO_MOVE_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 typename conditional<
+ has_signature<
+ typename traits_type::receive_cancelled_signature,
+ Signatures...
+ >::value,
+ typename conditional<
+ has_signature<
+ typename traits_type::receive_closed_signature,
+ Signatures...
+ >::value,
+ channel_payload<Signatures...>,
+ channel_payload<
+ Signatures...,
+ typename traits_type::receive_closed_signature
+ >
+ >::type,
+ typename conditional<
+ has_signature<
+ typename traits_type::receive_closed_signature,
+ Signatures...,
+ typename traits_type::receive_cancelled_signature
+ >::value,
+ channel_payload<
+ Signatures...,
+ typename traits_type::receive_cancelled_signature
+ >,
+ channel_payload<
+ Signatures...,
+ typename traits_type::receive_cancelled_signature,
+ typename traits_type::receive_closed_signature
+ >
+ >::type
+ >::type payload_type;
+
+ // Move from another buffer.
+ void buffer_move_from(implementation_type& other)
+ {
+ buffer_ = ASIO_MOVE_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(ASIO_MOVE_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 ASIO_MOVE_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 typename conditional<
+ has_signature<
+ typename traits_type::receive_cancelled_signature,
+ R()
+ >::value,
+ typename conditional<
+ has_signature<
+ typename traits_type::receive_closed_signature,
+ R()
+ >::value,
+ channel_payload<R()>,
+ channel_payload<
+ R(),
+ typename traits_type::receive_closed_signature
+ >
+ >::type,
+ typename conditional<
+ has_signature<
+ typename traits_type::receive_closed_signature,
+ R(),
+ typename traits_type::receive_cancelled_signature
+ >::value,
+ channel_payload<
+ R(),
+ typename traits_type::receive_cancelled_signature
+ >,
+ channel_payload<
+ R(),
+ typename traits_type::receive_cancelled_signature,
+ typename traits_type::receive_closed_signature
+ >
+ >::type
+ >::type 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(channel_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_;
+};
+
+} // 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
diff --git a/asio/include/asio/experimental/detail/has_signature.hpp b/asio/include/asio/experimental/detail/has_signature.hpp
new file mode 100644
index 0000000..ae72e1d
--- /dev/null
+++ b/asio/include/asio/experimental/detail/has_signature.hpp
@@ -0,0 +1,54 @@
+//
+// experimental/detail/has_signature.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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
diff --git a/asio/include/asio/experimental/detail/impl/channel_service.hpp b/asio/include/asio/experimental/detail/impl/channel_service.hpp
new file mode 100644
index 0000000..616bd45
--- /dev/null
+++ b/asio/include/asio/experimental/detail/impl/channel_service.hpp
@@ -0,0 +1,611 @@
+//
+// experimental/detail/impl/channel_service.hpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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(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 ASIO_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 ASIO_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_);
+
+ if (impl.receive_state_ == closed)
+ impl.receive_state_ = block;
+ if (impl.send_state_ == closed)
+ 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())
+ {
+ traits_type::invoke_receive_closed(
+ complete_receive<payload_type,
+ typename traits_type::receive_closed_signature>(
+ static_cast<channel_receive<payload_type>*>(op)));
+ impl.waiters_.pop();
+ }
+ }
+
+ impl.send_state_ = closed;
+ 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
+ {
+ traits_type::invoke_receive_cancelled(
+ complete_receive<payload_type,
+ typename traits_type::receive_cancelled_signature>(
+ static_cast<channel_receive<payload_type>*>(op)));
+ impl.waiters_.pop();
+ }
+ }
+
+ if (impl.receive_state_ == waiter)
+ impl.receive_state_ = block;
+ if (impl.send_state_ == waiter)
+ impl.send_state_ = 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
+ {
+ traits_type::invoke_receive_cancelled(
+ complete_receive<payload_type,
+ typename traits_type::receive_cancelled_signature>(
+ static_cast<channel_receive<payload_type>*>(op)));
+ impl.waiters_.pop();
+ }
+ }
+ 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_ = block;
+ }
+}
+
+template <typename Mutex>
+inline bool channel_service<Mutex>::ready(
+ const channel_service<Mutex>::base_implementation_type& impl)
+ const ASIO_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,
+ ASIO_MOVE_ARG(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, ASIO_MOVE_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, ASIO_MOVE_CAST(Args)(args)...));
+ channel_receive<payload_type>* receive_op =
+ static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
+ impl.waiters_.pop();
+ receive_op->complete(ASIO_MOVE_CAST(payload_type)(payload));
+ if (impl.waiters_.empty())
+ impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
+ 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, ASIO_MOVE_ARG(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, ASIO_MOVE_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,
+ ASIO_MOVE_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();
+ receive_op->complete(payload);
+ if (impl.waiters_.empty())
+ impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
+ 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->complete();
+ break;
+ }
+ case waiter:
+ {
+ channel_receive<payload_type>* receive_op =
+ static_cast<channel_receive<payload_type>*>(impl.waiters_.front());
+ impl.waiters_.pop();
+ receive_op->complete(send_op->get_payload());
+ if (impl.waiters_.empty())
+ impl.send_state_ = impl.max_buffer_size_ ? buffer : block;
+ send_op->complete();
+ 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,
+ ASIO_MOVE_ARG(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->complete();
+ }
+ 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);
+ channel_handler<payload_type, typename decay<Handler>::type>(
+ ASIO_MOVE_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();
+ send_op->complete();
+ if (impl.waiters_.front() == 0)
+ impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
+ lock.unlock();
+ asio::detail::non_const_lvalue<Handler> handler2(handler);
+ channel_handler<payload_type, typename decay<Handler>::type>(
+ ASIO_MOVE_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:
+ {
+ receive_op->complete(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->complete();
+ }
+ 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;
+ }
+ 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();
+ send_op->complete();
+ receive_op->complete(ASIO_MOVE_CAST(payload_type)(payload));
+ if (impl.waiters_.front() == 0)
+ impl.receive_state_ = (impl.send_state_ == closed) ? closed : block;
+ break;
+ }
+ case closed:
+ default:
+ {
+ traits_type::invoke_receive_closed(
+ complete_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
diff --git a/asio/include/asio/experimental/impl/channel_error.ipp b/asio/include/asio/experimental/impl/channel_error.ipp
new file mode 100644
index 0000000..c30e024
--- /dev/null
+++ b/asio/include/asio/experimental/impl/channel_error.ipp
@@ -0,0 +1,61 @@
+//
+// experimental/impl/channel_error.ipp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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_IMPL_CHANNEL_ERROR_IPP
+#define ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+# pragma once
+#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "asio/detail/config.hpp"
+#include "asio/experimental/channel_error.hpp"
+
+#include "asio/detail/push_options.hpp"
+
+namespace asio {
+namespace experimental {
+namespace error {
+namespace detail {
+
+class channel_category : public asio::error_category
+{
+public:
+ const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT
+ {
+ return "asio.channel";
+ }
+
+ std::string message(int value) const
+ {
+ switch (value)
+ {
+ case channel_closed: return "Channel closed";
+ case channel_cancelled: return "Channel cancelled";
+ default: return "asio.channel error";
+ }
+ }
+};
+
+} // namespace detail
+
+const asio::error_category& get_channel_category()
+{
+ static detail::channel_category instance;
+ return instance;
+}
+
+} // namespace error
+} // namespace experimental
+} // namespace asio
+
+#include "asio/detail/pop_options.hpp"
+
+#endif // ASIO_EXPERIMENTAL_IMPL_CHANNEL_ERROR_IPP
diff --git a/asio/include/asio/impl/src.hpp b/asio/include/asio/impl/src.hpp
index 503bbb6..9d159eb 100644
--- a/asio/include/asio/impl/src.hpp
+++ b/asio/include/asio/impl/src.hpp
@@ -81,6 +81,7 @@
#include "asio/detail/impl/winsock_init.ipp"
#include "asio/execution/impl/bad_executor.ipp"
#include "asio/execution/impl/receiver_invocation_error.ipp"
+#include "asio/experimental/impl/channel_error.ipp"
#include "asio/generic/detail/impl/endpoint.ipp"
#include "asio/ip/impl/address.ipp"
#include "asio/ip/impl/address_v4.ipp"
diff --git a/asio/src/doc/quickref.xml b/asio/src/doc/quickref.xml
index 8853b74..d1dc9b2 100644
--- a/asio/src/doc/quickref.xml
+++ b/asio/src/doc/quickref.xml
@@ -272,6 +272,9 @@
<member><link linkend="asio.reference.experimental__append_t">experimental::append_t</link></member>
<member><link linkend="asio.reference.experimental__as_single_t">experimental::as_single_t</link></member>
<member><link linkend="asio.reference.experimental__as_tuple_t">experimental::as_tuple_t</link></member>
+ <member><link linkend="asio.reference.experimental__basic_channel">experimental::basic_channel</link></member>
+ <member><link linkend="asio.reference.experimental__basic_concurrent_channel">experimental::basic_concurrent_channel</link></member>
+ <member><link linkend="asio.reference.experimental__channel_traits">experimental::channel_traits</link></member>
<member><link linkend="asio.reference.experimental__coro">experimental::coro</link></member>
<member><link linkend="asio.reference.experimental__parallel_group">experimental::parallel_group</link></member>
<member><link linkend="asio.reference.experimental__prepend_t">experimental::prepend_t</link></member>
diff --git a/asio/src/doc/reference.qbk b/asio/src/doc/reference.qbk
index 1014945..9a2cd6d 100644
--- a/asio/src/doc/reference.qbk
+++ b/asio/src/doc/reference.qbk
@@ -90946,6 +90946,1894 @@
[endsect]
+[section:experimental__basic_channel experimental::basic_channel]
+
+[indexterm1 asio.indexterm.experimental__basic_channel..experimental::basic_channel]
+
+
+A channel for messages.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor]``,
+ typename Traits,
+ typename... Signatures>
+ class basic_channel
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel__rebind_executor [*rebind_executor]]]
+ [Rebinds the channel type to another executor. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel.executor_type [*executor_type]]]
+ [The type of the executor associated with the channel. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel.traits_type [*traits_type]]]
+ [The traits type associated with the channel. ]
+
+ ]
+
+]
+
+[heading Member Functions]
+[table
+ [[Name][Description]]
+
+ [
+ [[link asio.reference.experimental__basic_channel.async_receive [*async_receive]]]
+ [Asynchronously receive a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.async_send [*async_send]]]
+ [Asynchronously send a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.basic_channel [*basic_channel]] [constructor]]
+ [Construct a basic_channel.
+ [hr]
+ Construct and open a basic_channel.
+ [hr]
+ Move-construct a basic_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.cancel [*cancel]]]
+ [Cancel all asynchronous operations waiting on the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.capacity [*capacity]]]
+ [Get the capacity of the channel's buffer. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.close [*close]]]
+ [Close the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.get_executor [*get_executor]]]
+ [Get the executor associated with the object. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.is_open [*is_open]]]
+ [Determine whether the channel is open. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.operator_eq_ [*operator=]]]
+ [Move-assign a basic_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.ready [*ready]]]
+ [Determine whether a message can be received without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.reset [*reset]]]
+ [Reset the channel to its initial state. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_receive [*try_receive]]]
+ [Try to receive a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_send [*try_send]]]
+ [Try to send a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_send_n [*try_send_n]]]
+ [Try to send a number of messages without blocking. ]
+ ]
+
+]
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_channel.hpp]
+
+['Convenience header: ]None
+
+
+[section:async_receive experimental::basic_channel::async_receive]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.async_receive..async_receive..experimental::basic_channel]
+Asynchronously receive a message.
+
+
+ template<
+ typename CompletionToken = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``>
+ auto async_receive(
+ CompletionToken && token = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``);
+
+
+
+[endsect]
+
+
+
+[section:async_send experimental::basic_channel::async_send]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.async_send..async_send..experimental::basic_channel]
+Asynchronously send a message.
+
+
+ template<
+ typename... Args,
+ typename CompletionToken = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``>
+ auto async_send(
+ Args &&... args,
+ CompletionToken && token);
+
+
+
+[endsect]
+
+
+[section:basic_channel experimental::basic_channel::basic_channel]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.basic_channel..basic_channel..experimental::basic_channel]
+Construct a [link asio.reference.experimental__basic_channel `experimental::basic_channel`].
+
+
+ ``[link asio.reference.experimental__basic_channel.basic_channel.overload1 basic_channel]``(
+ const executor_type & ex,
+ std::size_t max_buffer_size = 0);
+ `` [''''»''' [link asio.reference.experimental__basic_channel.basic_channel.overload1 more...]]``
+
+
+Construct and open a [link asio.reference.experimental__basic_channel `experimental::basic_channel`].
+
+
+ template<
+ typename ExecutionContext>
+ ``[link asio.reference.experimental__basic_channel.basic_channel.overload2 basic_channel]``(
+ ExecutionContext & context,
+ std::size_t max_buffer_size = 0,
+ typename constraint< is_convertible< ExecutionContext &, execution_context & >::value, defaulted_constraint >::type = defaulted_constraint());
+ `` [''''»''' [link asio.reference.experimental__basic_channel.basic_channel.overload2 more...]]``
+
+
+Move-construct a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ ``[link asio.reference.experimental__basic_channel.basic_channel.overload3 basic_channel]``(
+ basic_channel && other);
+ `` [''''»''' [link asio.reference.experimental__basic_channel.basic_channel.overload3 more...]]``
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ ``[link asio.reference.experimental__basic_channel.basic_channel.overload4 basic_channel]``(
+ basic_channel< Executor1, Traits, Signatures...> && other,
+ typename constraint< is_convertible< Executor1, Executor >::value >::type = 0);
+ `` [''''»''' [link asio.reference.experimental__basic_channel.basic_channel.overload4 more...]]``
+
+
+[section:overload1 experimental::basic_channel::basic_channel (1 of 4 overloads)]
+
+
+Construct a [link asio.reference.experimental__basic_channel `experimental::basic_channel`].
+
+
+ basic_channel(
+ const executor_type & ex,
+ std::size_t max_buffer_size = 0);
+
+
+This constructor creates and channel.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[ex][The I/O executor that the channel will use, by default, to dispatch handlers for any asynchronous operations performed on the channel.]]
+
+[[max_buffer_size][The maximum number of messages that may be buffered in the channel. ]]
+
+]
+
+
+
+
+[endsect]
+
+
+
+[section:overload2 experimental::basic_channel::basic_channel (2 of 4 overloads)]
+
+
+Construct and open a [link asio.reference.experimental__basic_channel `experimental::basic_channel`].
+
+
+ template<
+ typename ExecutionContext>
+ basic_channel(
+ ExecutionContext & context,
+ std::size_t max_buffer_size = 0,
+ typename constraint< is_convertible< ExecutionContext &, execution_context & >::value, defaulted_constraint >::type = defaulted_constraint());
+
+
+This constructor creates and opens a channel.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[context][An execution context which provides the I/O executor that the channel will use, by default, to dispatch handlers for any asynchronous operations performed on the channel.]]
+
+[[max_buffer_size][The maximum number of messages that may be buffered in the channel. ]]
+
+]
+
+
+
+
+[endsect]
+
+
+
+[section:overload3 experimental::basic_channel::basic_channel (3 of 4 overloads)]
+
+
+Move-construct a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ basic_channel(
+ basic_channel && other);
+
+
+This constructor moves a channel from one object to another.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_channel `experimental::basic_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_channel(const executor_type&) constructor`.
+
+
+
+
+[endsect]
+
+
+
+[section:overload4 experimental::basic_channel::basic_channel (4 of 4 overloads)]
+
+
+Move-construct a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ basic_channel(
+ basic_channel< Executor1, Traits, Signatures...> && other,
+ typename constraint< is_convertible< Executor1, Executor >::value >::type = 0);
+
+
+This constructor moves a channel from one object to another.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_channel `experimental::basic_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+[endsect]
+
+
+[section:cancel experimental::basic_channel::cancel]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.cancel..cancel..experimental::basic_channel]
+Cancel all asynchronous operations waiting on the channel.
+
+
+ void cancel();
+
+
+All outstanding send operations will complete with the error `asio::experimental::error::channel_canceld`. Outstanding receive operations complete with the result as determined by the channel traits.
+
+
+[endsect]
+
+
+
+[section:capacity experimental::basic_channel::capacity]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.capacity..capacity..experimental::basic_channel]
+Get the capacity of the channel's buffer.
+
+
+ std::size_t capacity();
+
+
+
+[endsect]
+
+
+
+[section:close experimental::basic_channel::close]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.close..close..experimental::basic_channel]
+Close the channel.
+
+
+ void close();
+
+
+
+[endsect]
+
+
+
+[section:executor_type experimental::basic_channel::executor_type]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.executor_type..executor_type..experimental::basic_channel]
+The type of the executor associated with the channel.
+
+
+ typedef Executor executor_type;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:get_executor experimental::basic_channel::get_executor]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.get_executor..get_executor..experimental::basic_channel]
+Get the executor associated with the object.
+
+
+ executor_type get_executor();
+
+
+
+[endsect]
+
+
+
+[section:is_open experimental::basic_channel::is_open]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.is_open..is_open..experimental::basic_channel]
+Determine whether the channel is open.
+
+
+ bool is_open() const;
+
+
+
+[endsect]
+
+
+[section:operator_eq_ experimental::basic_channel::operator=]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.operator_eq_..operator=..experimental::basic_channel]
+Move-assign a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ basic_channel & ``[link asio.reference.experimental__basic_channel.operator_eq_.overload1 operator=]``(
+ basic_channel && other);
+ `` [''''»''' [link asio.reference.experimental__basic_channel.operator_eq_.overload1 more...]]``
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ constraint< is_convertible< Executor1, Executor >::value, basic_channel & >::type ``[link asio.reference.experimental__basic_channel.operator_eq_.overload2 operator=]``(
+ basic_channel< Executor1, Traits, Signatures...> && other);
+ `` [''''»''' [link asio.reference.experimental__basic_channel.operator_eq_.overload2 more...]]``
+
+
+[section:overload1 experimental::basic_channel::operator= (1 of 2 overloads)]
+
+
+Move-assign a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ basic_channel & operator=(
+ basic_channel && other);
+
+
+This assignment operator moves a channel from one object to another. Cancels any outstanding asynchronous operations associated with the target object.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_channel `experimental::basic_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+
+[section:overload2 experimental::basic_channel::operator= (2 of 2 overloads)]
+
+
+Move-assign a [link asio.reference.experimental__basic_channel `experimental::basic_channel`] from another.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ constraint< is_convertible< Executor1, Executor >::value, basic_channel & >::type operator=(
+ basic_channel< Executor1, Traits, Signatures...> && other);
+
+
+This assignment operator moves a channel from one object to another. Cancels any outstanding asynchronous operations associated with the target object.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_channel `experimental::basic_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+[endsect]
+
+
+[section:ready experimental::basic_channel::ready]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.ready..ready..experimental::basic_channel]
+Determine whether a message can be received without blocking.
+
+
+ bool ready() const;
+
+
+
+[endsect]
+
+
+
+[section:reset experimental::basic_channel::reset]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.reset..reset..experimental::basic_channel]
+Reset the channel to its initial state.
+
+
+ void reset();
+
+
+
+[endsect]
+
+
+
+[section:traits_type experimental::basic_channel::traits_type]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.traits_type..traits_type..experimental::basic_channel]
+The traits type associated with the channel.
+
+
+ typedef Traits::template rebind< Signatures...>::other traits_type;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:try_receive experimental::basic_channel::try_receive]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.try_receive..try_receive..experimental::basic_channel]
+Try to receive a message without blocking.
+
+
+ template<
+ typename ``[link asio.reference.Handler Handler]``>
+ bool try_receive(
+ Handler && handler);
+
+
+Fails if the buffer is full and there are no waiting receive operations.
+
+
+[heading Return Value]
+
+`true` on success, `false` on failure.
+
+
+
+
+[endsect]
+
+
+
+[section:try_send experimental::basic_channel::try_send]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.try_send..try_send..experimental::basic_channel]
+Try to send a message without blocking.
+
+
+ template<
+ typename... Args>
+ bool try_send(
+ Args &&... args);
+
+
+Fails if the buffer is full and there are no waiting receive operations.
+
+
+[heading Return Value]
+
+`true` on success, `false` on failure.
+
+
+
+
+[endsect]
+
+
+
+[section:try_send_n experimental::basic_channel::try_send_n]
+
+[indexterm2 asio.indexterm.experimental__basic_channel.try_send_n..try_send_n..experimental::basic_channel]
+Try to send a number of messages without blocking.
+
+
+ template<
+ typename... Args>
+ std::size_t try_send_n(
+ std::size_t count,
+ Args &&... args);
+
+
+
+[heading Return Value]
+
+The number of messages that were sent.
+
+
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__basic_channel__rebind_executor experimental::basic_channel::rebind_executor]
+
+[indexterm1 asio.indexterm.experimental__basic_channel__rebind_executor..experimental::basic_channel::rebind_executor]
+
+
+Rebinds the channel type to another executor.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ struct rebind_executor
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel__rebind_executor.other [*other]]]
+ [The channel type when rebound to the specified executor. ]
+
+ ]
+
+]
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_channel.hpp]
+
+['Convenience header: ]None
+
+
+[section:other experimental::basic_channel::rebind_executor::other]
+
+[indexterm2 asio.indexterm.experimental__basic_channel__rebind_executor.other..other..experimental::basic_channel::rebind_executor]
+The channel type when rebound to the specified executor.
+
+
+ typedef basic_channel< Executor1, Traits, Signatures...> other;
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel__rebind_executor [*rebind_executor]]]
+ [Rebinds the channel type to another executor. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel.executor_type [*executor_type]]]
+ [The type of the executor associated with the channel. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_channel.traits_type [*traits_type]]]
+ [The traits type associated with the channel. ]
+
+ ]
+
+]
+
+[heading Member Functions]
+[table
+ [[Name][Description]]
+
+ [
+ [[link asio.reference.experimental__basic_channel.async_receive [*async_receive]]]
+ [Asynchronously receive a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.async_send [*async_send]]]
+ [Asynchronously send a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.basic_channel [*basic_channel]] [constructor]]
+ [Construct a basic_channel.
+ [hr]
+ Construct and open a basic_channel.
+ [hr]
+ Move-construct a basic_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.cancel [*cancel]]]
+ [Cancel all asynchronous operations waiting on the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.capacity [*capacity]]]
+ [Get the capacity of the channel's buffer. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.close [*close]]]
+ [Close the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.get_executor [*get_executor]]]
+ [Get the executor associated with the object. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.is_open [*is_open]]]
+ [Determine whether the channel is open. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.operator_eq_ [*operator=]]]
+ [Move-assign a basic_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.ready [*ready]]]
+ [Determine whether a message can be received without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.reset [*reset]]]
+ [Reset the channel to its initial state. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_receive [*try_receive]]]
+ [Try to receive a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_send [*try_send]]]
+ [Try to send a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_channel.try_send_n [*try_send_n]]]
+ [Try to send a number of messages without blocking. ]
+ ]
+
+]
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__basic_concurrent_channel experimental::basic_concurrent_channel]
+
+[indexterm1 asio.indexterm.experimental__basic_concurrent_channel..experimental::basic_concurrent_channel]
+
+
+A channel for messages.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor]``,
+ typename Traits,
+ typename... Signatures>
+ class basic_concurrent_channel
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel__rebind_executor [*rebind_executor]]]
+ [Rebinds the channel type to another executor. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel.executor_type [*executor_type]]]
+ [The type of the executor associated with the channel. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel.traits_type [*traits_type]]]
+ [The traits type associated with the channel. ]
+
+ ]
+
+]
+
+[heading Member Functions]
+[table
+ [[Name][Description]]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.async_receive [*async_receive]]]
+ [Asynchronously receive a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.async_send [*async_send]]]
+ [Asynchronously send a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel [*basic_concurrent_channel]] [constructor]]
+ [Construct a basic_concurrent_channel.
+ [hr]
+ Construct and open a basic_concurrent_channel.
+ [hr]
+ Move-construct a basic_concurrent_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.cancel [*cancel]]]
+ [Cancel all asynchronous operations waiting on the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.capacity [*capacity]]]
+ [Get the capacity of the channel's buffer. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.close [*close]]]
+ [Close the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.get_executor [*get_executor]]]
+ [Get the executor associated with the object. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.is_open [*is_open]]]
+ [Determine whether the channel is open. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.operator_eq_ [*operator=]]]
+ [Move-assign a basic_concurrent_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.ready [*ready]]]
+ [Determine whether a message can be received without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.reset [*reset]]]
+ [Reset the channel to its initial state. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_receive [*try_receive]]]
+ [Try to receive a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_send [*try_send]]]
+ [Try to send a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_send_n [*try_send_n]]]
+ [Try to send a number of messages without blocking. ]
+ ]
+
+]
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_concurrent_channel.hpp]
+
+['Convenience header: ]None
+
+
+[section:async_receive experimental::basic_concurrent_channel::async_receive]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.async_receive..async_receive..experimental::basic_concurrent_channel]
+Asynchronously receive a message.
+
+
+ template<
+ typename CompletionToken = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``>
+ auto async_receive(
+ CompletionToken && token = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``);
+
+
+
+[endsect]
+
+
+
+[section:async_send experimental::basic_concurrent_channel::async_send]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.async_send..async_send..experimental::basic_concurrent_channel]
+Asynchronously send a message.
+
+
+ template<
+ typename... Args,
+ typename CompletionToken = ``[link asio.reference.asynchronous_operations.default_completion_tokens ['DEFAULT]]``>
+ auto async_send(
+ Args &&... args,
+ CompletionToken && token);
+
+
+
+[endsect]
+
+
+[section:basic_concurrent_channel experimental::basic_concurrent_channel::basic_concurrent_channel]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.basic_concurrent_channel..basic_concurrent_channel..experimental::basic_concurrent_channel]
+Construct a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`].
+
+
+ ``[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload1 basic_concurrent_channel]``(
+ const executor_type & ex,
+ std::size_t max_buffer_size = 0);
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload1 more...]]``
+
+
+Construct and open a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`].
+
+
+ template<
+ typename ExecutionContext>
+ ``[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload2 basic_concurrent_channel]``(
+ ExecutionContext & context,
+ std::size_t max_buffer_size = 0,
+ typename constraint< is_convertible< ExecutionContext &, execution_context & >::value, defaulted_constraint >::type = defaulted_constraint());
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload2 more...]]``
+
+
+Move-construct a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ ``[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload3 basic_concurrent_channel]``(
+ basic_concurrent_channel && other);
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload3 more...]]``
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ ``[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload4 basic_concurrent_channel]``(
+ basic_concurrent_channel< Executor1, Traits, Signatures...> && other,
+ typename constraint< is_convertible< Executor1, Executor >::value >::type = 0);
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel.overload4 more...]]``
+
+
+[section:overload1 experimental::basic_concurrent_channel::basic_concurrent_channel (1 of 4 overloads)]
+
+
+Construct a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`].
+
+
+ basic_concurrent_channel(
+ const executor_type & ex,
+ std::size_t max_buffer_size = 0);
+
+
+This constructor creates and channel.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[ex][The I/O executor that the channel will use, by default, to dispatch handlers for any asynchronous operations performed on the channel.]]
+
+[[max_buffer_size][The maximum number of messages that may be buffered in the channel. ]]
+
+]
+
+
+
+
+[endsect]
+
+
+
+[section:overload2 experimental::basic_concurrent_channel::basic_concurrent_channel (2 of 4 overloads)]
+
+
+Construct and open a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`].
+
+
+ template<
+ typename ExecutionContext>
+ basic_concurrent_channel(
+ ExecutionContext & context,
+ std::size_t max_buffer_size = 0,
+ typename constraint< is_convertible< ExecutionContext &, execution_context & >::value, defaulted_constraint >::type = defaulted_constraint());
+
+
+This constructor creates and opens a channel.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[context][An execution context which provides the I/O executor that the channel will use, by default, to dispatch handlers for any asynchronous operations performed on the channel.]]
+
+[[max_buffer_size][The maximum number of messages that may be buffered in the channel. ]]
+
+]
+
+
+
+
+[endsect]
+
+
+
+[section:overload3 experimental::basic_concurrent_channel::basic_concurrent_channel (3 of 4 overloads)]
+
+
+Move-construct a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ basic_concurrent_channel(
+ basic_concurrent_channel && other);
+
+
+This constructor moves a channel from one object to another.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_concurrent_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+
+[section:overload4 experimental::basic_concurrent_channel::basic_concurrent_channel (4 of 4 overloads)]
+
+
+Move-construct a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ basic_concurrent_channel(
+ basic_concurrent_channel< Executor1, Traits, Signatures...> && other,
+ typename constraint< is_convertible< Executor1, Executor >::value >::type = 0);
+
+
+This constructor moves a channel from one object to another.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_concurrent_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+[endsect]
+
+
+[section:cancel experimental::basic_concurrent_channel::cancel]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.cancel..cancel..experimental::basic_concurrent_channel]
+Cancel all asynchronous operations waiting on the channel.
+
+
+ void cancel();
+
+
+All outstanding send operations will complete with the error `asio::experimental::error::channel_canceld`. Outstanding receive operations complete with the result as determined by the channel traits.
+
+
+[endsect]
+
+
+
+[section:capacity experimental::basic_concurrent_channel::capacity]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.capacity..capacity..experimental::basic_concurrent_channel]
+Get the capacity of the channel's buffer.
+
+
+ std::size_t capacity();
+
+
+
+[endsect]
+
+
+
+[section:close experimental::basic_concurrent_channel::close]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.close..close..experimental::basic_concurrent_channel]
+Close the channel.
+
+
+ void close();
+
+
+
+[endsect]
+
+
+
+[section:executor_type experimental::basic_concurrent_channel::executor_type]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.executor_type..executor_type..experimental::basic_concurrent_channel]
+The type of the executor associated with the channel.
+
+
+ typedef Executor executor_type;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_concurrent_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:get_executor experimental::basic_concurrent_channel::get_executor]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.get_executor..get_executor..experimental::basic_concurrent_channel]
+Get the executor associated with the object.
+
+
+ executor_type get_executor();
+
+
+
+[endsect]
+
+
+
+[section:is_open experimental::basic_concurrent_channel::is_open]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.is_open..is_open..experimental::basic_concurrent_channel]
+Determine whether the channel is open.
+
+
+ bool is_open() const;
+
+
+
+[endsect]
+
+
+[section:operator_eq_ experimental::basic_concurrent_channel::operator=]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.operator_eq_..operator=..experimental::basic_concurrent_channel]
+Move-assign a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ basic_concurrent_channel & ``[link asio.reference.experimental__basic_concurrent_channel.operator_eq_.overload1 operator=]``(
+ basic_concurrent_channel && other);
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.operator_eq_.overload1 more...]]``
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ constraint< is_convertible< Executor1, Executor >::value, basic_concurrent_channel & >::type ``[link asio.reference.experimental__basic_concurrent_channel.operator_eq_.overload2 operator=]``(
+ basic_concurrent_channel< Executor1, Traits, Signatures...> && other);
+ `` [''''»''' [link asio.reference.experimental__basic_concurrent_channel.operator_eq_.overload2 more...]]``
+
+
+[section:overload1 experimental::basic_concurrent_channel::operator= (1 of 2 overloads)]
+
+
+Move-assign a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ basic_concurrent_channel & operator=(
+ basic_concurrent_channel && other);
+
+
+This assignment operator moves a channel from one object to another. Cancels any outstanding asynchronous operations associated with the target object.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_concurrent_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+
+[section:overload2 experimental::basic_concurrent_channel::operator= (2 of 2 overloads)]
+
+
+Move-assign a [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] from another.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ constraint< is_convertible< Executor1, Executor >::value, basic_concurrent_channel & >::type operator=(
+ basic_concurrent_channel< Executor1, Traits, Signatures...> && other);
+
+
+This assignment operator moves a channel from one object to another. Cancels any outstanding asynchronous operations associated with the target object.
+
+
+[heading Parameters]
+
+
+[variablelist
+
+[[other][The other [link asio.reference.experimental__basic_concurrent_channel `experimental::basic_concurrent_channel`] object from which the move will occur.]]
+
+]
+
+
+[heading Remarks]
+
+Following the move, the moved-from object is in the same state as if constructed using the `basic_concurrent_channel(const executor_type&)` constructor.
+
+
+
+
+[endsect]
+
+
+[endsect]
+
+
+[section:ready experimental::basic_concurrent_channel::ready]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.ready..ready..experimental::basic_concurrent_channel]
+Determine whether a message can be received without blocking.
+
+
+ bool ready() const;
+
+
+
+[endsect]
+
+
+
+[section:reset experimental::basic_concurrent_channel::reset]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.reset..reset..experimental::basic_concurrent_channel]
+Reset the channel to its initial state.
+
+
+ void reset();
+
+
+
+[endsect]
+
+
+
+[section:traits_type experimental::basic_concurrent_channel::traits_type]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.traits_type..traits_type..experimental::basic_concurrent_channel]
+The traits type associated with the channel.
+
+
+ typedef Traits::template rebind< Signatures...>::other traits_type;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_concurrent_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:try_receive experimental::basic_concurrent_channel::try_receive]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.try_receive..try_receive..experimental::basic_concurrent_channel]
+Try to receive a message without blocking.
+
+
+ template<
+ typename ``[link asio.reference.Handler Handler]``>
+ bool try_receive(
+ Handler && handler);
+
+
+Fails if the buffer is full and there are no waiting receive operations.
+
+
+[heading Return Value]
+
+`true` on success, `false` on failure.
+
+
+
+
+[endsect]
+
+
+
+[section:try_send experimental::basic_concurrent_channel::try_send]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.try_send..try_send..experimental::basic_concurrent_channel]
+Try to send a message without blocking.
+
+
+ template<
+ typename... Args>
+ bool try_send(
+ Args &&... args);
+
+
+Fails if the buffer is full and there are no waiting receive operations.
+
+
+[heading Return Value]
+
+`true` on success, `false` on failure.
+
+
+
+
+[endsect]
+
+
+
+[section:try_send_n experimental::basic_concurrent_channel::try_send_n]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel.try_send_n..try_send_n..experimental::basic_concurrent_channel]
+Try to send a number of messages without blocking.
+
+
+ template<
+ typename... Args>
+ std::size_t try_send_n(
+ std::size_t count,
+ Args &&... args);
+
+
+
+[heading Return Value]
+
+The number of messages that were sent.
+
+
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__basic_concurrent_channel__rebind_executor experimental::basic_concurrent_channel::rebind_executor]
+
+[indexterm1 asio.indexterm.experimental__basic_concurrent_channel__rebind_executor..experimental::basic_concurrent_channel::rebind_executor]
+
+
+Rebinds the channel type to another executor.
+
+
+ template<
+ typename ``[link asio.reference.Executor1 Executor1]``>
+ struct rebind_executor
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel__rebind_executor.other [*other]]]
+ [The channel type when rebound to the specified executor. ]
+
+ ]
+
+]
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_concurrent_channel.hpp]
+
+['Convenience header: ]None
+
+
+[section:other experimental::basic_concurrent_channel::rebind_executor::other]
+
+[indexterm2 asio.indexterm.experimental__basic_concurrent_channel__rebind_executor.other..other..experimental::basic_concurrent_channel::rebind_executor]
+The channel type when rebound to the specified executor.
+
+
+ typedef basic_concurrent_channel< Executor1, Traits, Signatures...> other;
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel__rebind_executor [*rebind_executor]]]
+ [Rebinds the channel type to another executor. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel.executor_type [*executor_type]]]
+ [The type of the executor associated with the channel. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__basic_concurrent_channel.traits_type [*traits_type]]]
+ [The traits type associated with the channel. ]
+
+ ]
+
+]
+
+[heading Member Functions]
+[table
+ [[Name][Description]]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.async_receive [*async_receive]]]
+ [Asynchronously receive a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.async_send [*async_send]]]
+ [Asynchronously send a message. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.basic_concurrent_channel [*basic_concurrent_channel]] [constructor]]
+ [Construct a basic_concurrent_channel.
+ [hr]
+ Construct and open a basic_concurrent_channel.
+ [hr]
+ Move-construct a basic_concurrent_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.cancel [*cancel]]]
+ [Cancel all asynchronous operations waiting on the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.capacity [*capacity]]]
+ [Get the capacity of the channel's buffer. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.close [*close]]]
+ [Close the channel. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.get_executor [*get_executor]]]
+ [Get the executor associated with the object. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.is_open [*is_open]]]
+ [Determine whether the channel is open. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.operator_eq_ [*operator=]]]
+ [Move-assign a basic_concurrent_channel from another. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.ready [*ready]]]
+ [Determine whether a message can be received without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.reset [*reset]]]
+ [Reset the channel to its initial state. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_receive [*try_receive]]]
+ [Try to receive a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_send [*try_send]]]
+ [Try to send a message without blocking. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__basic_concurrent_channel.try_send_n [*try_send_n]]]
+ [Try to send a number of messages without blocking. ]
+ ]
+
+]
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/basic_concurrent_channel.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__channel_traits experimental::channel_traits]
+
+[indexterm1 asio.indexterm.experimental__channel_traits..experimental::channel_traits]
+
+
+
+ template<
+ typename... Signatures>
+ struct channel_traits
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits__container [*container]]]
+ [Determine the container for the specified elements. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits__rebind [*rebind]]]
+ [Rebind the traits to a new set of signatures. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits.receive_cancelled_signature [*receive_cancelled_signature]]]
+ [The signature of a channel cancellation notification. ]
+
+ ]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits.receive_closed_signature [*receive_closed_signature]]]
+ [The signature of a channel closed notification. ]
+
+ ]
+
+]
+
+[heading Member Functions]
+[table
+ [[Name][Description]]
+
+ [
+ [[link asio.reference.experimental__channel_traits.invoke_receive_cancelled [*invoke_receive_cancelled]] [static]]
+ [Invoke the specified handler with a cancellation notification. ]
+ ]
+
+ [
+ [[link asio.reference.experimental__channel_traits.invoke_receive_closed [*invoke_receive_closed]] [static]]
+ [Invoke the specified handler with a closed notification. ]
+ ]
+
+]
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[section:invoke_receive_cancelled experimental::channel_traits::invoke_receive_cancelled]
+
+[indexterm2 asio.indexterm.experimental__channel_traits.invoke_receive_cancelled..invoke_receive_cancelled..experimental::channel_traits]
+Invoke the specified handler with a cancellation notification.
+
+
+ template<
+ typename F>
+ static void invoke_receive_cancelled(
+ F f);
+
+
+
+[endsect]
+
+
+
+[section:invoke_receive_closed experimental::channel_traits::invoke_receive_closed]
+
+[indexterm2 asio.indexterm.experimental__channel_traits.invoke_receive_closed..invoke_receive_closed..experimental::channel_traits]
+Invoke the specified handler with a closed notification.
+
+
+ template<
+ typename F>
+ static void invoke_receive_closed(
+ F f);
+
+
+
+[endsect]
+
+
+
+[section:receive_cancelled_signature experimental::channel_traits::receive_cancelled_signature]
+
+[indexterm2 asio.indexterm.experimental__channel_traits.receive_cancelled_signature..receive_cancelled_signature..experimental::channel_traits]
+The signature of a channel cancellation notification.
+
+
+ typedef void receive_cancelled_signature;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:receive_closed_signature experimental::channel_traits::receive_closed_signature]
+
+[indexterm2 asio.indexterm.experimental__channel_traits.receive_closed_signature..receive_closed_signature..experimental::channel_traits]
+The signature of a channel closed notification.
+
+
+ typedef void receive_closed_signature;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__channel_traits__container experimental::channel_traits::container]
+
+[indexterm1 asio.indexterm.experimental__channel_traits__container..experimental::channel_traits::container]
+
+
+Determine the container for the specified elements.
+
+
+ template<
+ typename Element>
+ struct container
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits__container.type [*type]]]
+ []
+
+ ]
+
+]
+
+This nested structure must have a single nested type `other` that aliases a container type for the specified element type.
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[section:type experimental::channel_traits::container::type]
+
+[indexterm2 asio.indexterm.experimental__channel_traits__container.type..type..experimental::channel_traits::container]
+
+ typedef user_defined type;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[endsect]
+
+[section:experimental__channel_traits__rebind experimental::channel_traits::rebind]
+
+[indexterm1 asio.indexterm.experimental__channel_traits__rebind..experimental::channel_traits::rebind]
+
+
+Rebind the traits to a new set of signatures.
+
+
+ template<
+ typename... NewSignatures>
+ struct rebind
+
+
+[heading Types]
+[table
+ [[Name][Description]]
+
+ [
+
+ [[link asio.reference.experimental__channel_traits__rebind.other [*other]]]
+ []
+
+ ]
+
+]
+
+This nested structure must have a single nested type `other` that aliases a traits type with the specified set of signatures.
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[section:other experimental::channel_traits::rebind::other]
+
+[indexterm2 asio.indexterm.experimental__channel_traits__rebind.other..other..experimental::channel_traits::rebind]
+
+ typedef user_defined other;
+
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_traits.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[endsect]
+
[section:experimental__co_spawn experimental::co_spawn]
[indexterm1 asio.indexterm.experimental__co_spawn..experimental::co_spawn]
@@ -92600,6 +94488,96 @@
[endsect]
+
+[section:experimental__error__channel_category experimental::error::channel_category]
+
+[indexterm1 asio.indexterm.experimental__error__channel_category..experimental::error::channel_category]
+
+ static const asio::error_category & channel_category = asio::experimental::error::get_channel_category();
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_error.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:experimental__error__channel_errors experimental::error::channel_errors]
+
+[indexterm1 asio.indexterm.experimental__error__channel_errors..experimental::error::channel_errors]
+ enum channel_errors
+
+[indexterm2 asio.indexterm.experimental__error__channel_errors.channel_closed..channel_closed..experimental::error::channel_errors]
+[indexterm2 asio.indexterm.experimental__error__channel_errors.channel_cancelled..channel_cancelled..experimental::error::channel_errors]
+
+[heading Values]
+[variablelist
+
+ [
+ [channel_closed]
+ [The channel was closed. ]
+ ]
+
+ [
+ [channel_cancelled]
+ [The channel was cancelled. ]
+ ]
+
+]
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_error.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:experimental__error__get_channel_category experimental::error::get_channel_category]
+
+[indexterm1 asio.indexterm.experimental__error__get_channel_category..experimental::error::get_channel_category]
+
+ const asio::error_category & get_channel_category();
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_error.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
+
+[section:experimental__error__make_error_code experimental::error::make_error_code]
+
+[indexterm1 asio.indexterm.experimental__error__make_error_code..experimental::error::make_error_code]
+
+ asio::error_code make_error_code(
+ channel_errors e);
+
+
+[heading Requirements]
+
+['Header: ][^asio/experimental/channel_error.hpp]
+
+['Convenience header: ]None
+
+
+[endsect]
+
+
[section:experimental__is_deferred experimental::is_deferred]
[indexterm1 asio.indexterm.experimental__is_deferred..experimental::is_deferred]
diff --git a/asio/src/doc/reference.xsl b/asio/src/doc/reference.xsl
index b73033a..01f5b9f 100644
--- a/asio/src/doc/reference.xsl
+++ b/asio/src/doc/reference.xsl
@@ -1634,6 +1634,9 @@
<xsl:when test="declname = 'E'">
<xsl:value-of select="declname"/>
</xsl:when>
+ <xsl:when test="declname = 'Element'">
+ <xsl:value-of select="declname"/>
+ </xsl:when>
<xsl:when test="declname = 'EndpointIterator'">
<xsl:value-of select="declname"/>
</xsl:when>
@@ -1715,6 +1718,9 @@
<xsl:when test="declname = 'N'">
<xsl:value-of select="declname"/>
</xsl:when>
+ <xsl:when test="declname = 'NewSignatures'">
+ <xsl:value-of select="declname"/>
+ </xsl:when>
<xsl:when test="declname = 'OnFalse'">
<xsl:value-of select="declname"/>
</xsl:when>
diff --git a/asio/src/tests/Makefile.am b/asio/src/tests/Makefile.am
index 54702ce..235e837 100644
--- a/asio/src/tests/Makefile.am
+++ b/asio/src/tests/Makefile.am
@@ -191,6 +191,15 @@
unit/experimental/deferred
endif
+if HAVE_CXX17
+check_PROGRAMS += \
+ unit/experimental/basic_channel \
+ unit/experimental/basic_concurrent_channel \
+ unit/experimental/channel \
+ unit/experimental/channel_traits \
+ unit/experimental/concurrent_channel
+endif
+
if HAVE_CXX20
check_PROGRAMS += \
unit/experimental/promise
@@ -384,6 +393,15 @@
unit/experimental/deferred
endif
+if HAVE_CXX17
+TESTS += \
+ unit/experimental/basic_channel \
+ unit/experimental/basic_concurrent_channel \
+ unit/experimental/channel \
+ unit/experimental/channel_traits \
+ unit/experimental/concurrent_channel
+endif
+
if HAVE_CXX20
TESTS += \
unit/experimental/promise
@@ -596,6 +614,14 @@
unit_experimental_deferred_SOURCES = unit/experimental/deferred.cpp
endif
+if HAVE_CXX17
+unit_experimental_basic_channel_SOURCES = unit/experimental/basic_channel.cpp
+unit_experimental_basic_concurrent_channel_SOURCES = unit/experimental/basic_concurrent_channel.cpp
+unit_experimental_channel_SOURCES = unit/experimental/channel.cpp
+unit_experimental_channel_traits_SOURCES = unit/experimental/channel_traits.cpp
+unit_experimental_concurrent_channel_SOURCES = unit/experimental/concurrent_channel.cpp
+endif
+
if HAVE_CXX20
unit_experimental_promise_SOURCES = unit/experimental/promise.cpp
diff --git a/asio/src/tests/unit/experimental/.gitignore b/asio/src/tests/unit/experimental/.gitignore
index 71dfdfd..aca939b 100644
--- a/asio/src/tests/unit/experimental/.gitignore
+++ b/asio/src/tests/unit/experimental/.gitignore
@@ -8,5 +8,10 @@
*.pdb
*.tds
awaitable_operators
+basic_channel
+basic_concurrent_channel
+channel
+channel_traits
+concurrent_channel
deferred
promise
diff --git a/asio/src/tests/unit/experimental/basic_channel.cpp b/asio/src/tests/unit/experimental/basic_channel.cpp
new file mode 100644
index 0000000..4bb4134
--- /dev/null
+++ b/asio/src/tests/unit/experimental/basic_channel.cpp
@@ -0,0 +1,25 @@
+//
+// experimental/basic_channel.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include "asio/experimental/basic_channel.hpp"
+
+#include "../unit_test.hpp"
+
+ASIO_TEST_SUITE
+(
+ "experimental/basic_channel",
+ ASIO_TEST_CASE(null_test)
+)
diff --git a/asio/src/tests/unit/experimental/basic_concurrent_channel.cpp b/asio/src/tests/unit/experimental/basic_concurrent_channel.cpp
new file mode 100644
index 0000000..f0907b4
--- /dev/null
+++ b/asio/src/tests/unit/experimental/basic_concurrent_channel.cpp
@@ -0,0 +1,25 @@
+//
+// experimental/basic_concurrent_channel.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include "asio/experimental/basic_concurrent_channel.hpp"
+
+#include "../unit_test.hpp"
+
+ASIO_TEST_SUITE
+(
+ "experimental/basic_concurrent_channel",
+ ASIO_TEST_CASE(null_test)
+)
diff --git a/asio/src/tests/unit/experimental/channel.cpp b/asio/src/tests/unit/experimental/channel.cpp
new file mode 100644
index 0000000..19c20cc
--- /dev/null
+++ b/asio/src/tests/unit/experimental/channel.cpp
@@ -0,0 +1,165 @@
+//
+// experimental/channel.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include "asio/experimental/channel.hpp"
+
+#include <utility>
+#include "asio/error.hpp"
+#include "asio/io_context.hpp"
+#include "../unit_test.hpp"
+
+using namespace asio;
+using namespace asio::experimental;
+
+void unbuffered_channel_test()
+{
+ io_context ctx;
+
+ channel<void(asio::error_code, std::string)> ch1(ctx);
+
+ ASIO_CHECK(ch1.is_open());
+ ASIO_CHECK(!ch1.ready());
+
+ bool b1 = ch1.try_send(asio::error::eof, "hello");
+
+ ASIO_CHECK(!b1);
+
+ std::string s1 = "abcdefghijklmnopqrstuvwxyz";
+ bool b2 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(!b2);
+ ASIO_CHECK(!s1.empty());
+
+ asio::error_code ec1;
+ std::string s2;
+ ch1.async_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec1 = ec;
+ s2 = std::move(s);
+ });
+
+ bool b3 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(b3);
+ ASIO_CHECK(s1.empty());
+
+ ctx.run();
+
+ ASIO_CHECK(ec1 == asio::error::eof);
+ ASIO_CHECK(s2 == "abcdefghijklmnopqrstuvwxyz");
+
+ bool b4 = ch1.try_receive([](asio::error_code, std::string){});
+
+ ASIO_CHECK(!b4);
+
+ asio::error_code ec2 = asio::error::would_block;
+ std::string s3 = "zyxwvutsrqponmlkjihgfedcba";
+ ch1.async_send(asio::error::eof, std::move(s3),
+ [&](asio::error_code ec)
+ {
+ ec2 = ec;
+ });
+
+ asio::error_code ec3;
+ std::string s4;
+ bool b5 = ch1.try_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec3 = ec;
+ s4 = s;
+ });
+
+ ASIO_CHECK(b5);
+ ASIO_CHECK(ec3 == asio::error::eof);
+ ASIO_CHECK(s4 == "zyxwvutsrqponmlkjihgfedcba");
+
+ ctx.restart();
+ ctx.run();
+
+ ASIO_CHECK(!ec2);
+};
+
+void buffered_channel_test()
+{
+ io_context ctx;
+
+ channel<void(asio::error_code, std::string)> ch1(ctx, 1);
+
+ ASIO_CHECK(ch1.is_open());
+ ASIO_CHECK(!ch1.ready());
+
+ bool b1 = ch1.try_send(asio::error::eof, "hello");
+
+ ASIO_CHECK(b1);
+
+ std::string s1 = "abcdefghijklmnopqrstuvwxyz";
+ bool b2 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(!b2);
+ ASIO_CHECK(!s1.empty());
+
+ asio::error_code ec1;
+ std::string s2;
+ ch1.async_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec1 = ec;
+ s2 = std::move(s);
+ });
+
+ ctx.run();
+
+ ASIO_CHECK(ec1 == asio::error::eof);
+ ASIO_CHECK(s2 == "hello");
+
+ bool b4 = ch1.try_receive([](asio::error_code, std::string){});
+
+ ASIO_CHECK(!b4);
+
+ asio::error_code ec2 = asio::error::would_block;
+ std::string s3 = "zyxwvutsrqponmlkjihgfedcba";
+ ch1.async_send(asio::error::eof, std::move(s3),
+ [&](asio::error_code ec)
+ {
+ ec2 = ec;
+ });
+
+ asio::error_code ec3;
+ std::string s4;
+ bool b5 = ch1.try_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec3 = ec;
+ s4 = s;
+ });
+
+ ASIO_CHECK(b5);
+ ASIO_CHECK(ec3 == asio::error::eof);
+ ASIO_CHECK(s4 == "zyxwvutsrqponmlkjihgfedcba");
+
+ ctx.restart();
+ ctx.run();
+
+ ASIO_CHECK(!ec2);
+};
+
+ASIO_TEST_SUITE
+(
+ "experimental/channel",
+ ASIO_TEST_CASE(unbuffered_channel_test)
+ ASIO_TEST_CASE(buffered_channel_test)
+)
diff --git a/asio/src/tests/unit/experimental/channel_traits.cpp b/asio/src/tests/unit/experimental/channel_traits.cpp
new file mode 100644
index 0000000..ff1a04f
--- /dev/null
+++ b/asio/src/tests/unit/experimental/channel_traits.cpp
@@ -0,0 +1,25 @@
+//
+// experimental/channel_traits.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include "asio/experimental/channel_traits.hpp"
+
+#include "../unit_test.hpp"
+
+ASIO_TEST_SUITE
+(
+ "experimental/channel_traits",
+ ASIO_TEST_CASE(null_test)
+)
diff --git a/asio/src/tests/unit/experimental/concurrent_channel.cpp b/asio/src/tests/unit/experimental/concurrent_channel.cpp
new file mode 100644
index 0000000..7562efa
--- /dev/null
+++ b/asio/src/tests/unit/experimental/concurrent_channel.cpp
@@ -0,0 +1,165 @@
+//
+// experimental/concurrent_channel.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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)
+//
+
+// Disable autolinking for unit tests.
+#if !defined(BOOST_ALL_NO_LIB)
+#define BOOST_ALL_NO_LIB 1
+#endif // !defined(BOOST_ALL_NO_LIB)
+
+// Test that header file is self-contained.
+#include "asio/experimental/concurrent_channel.hpp"
+
+#include <utility>
+#include "asio/error.hpp"
+#include "asio/io_context.hpp"
+#include "../unit_test.hpp"
+
+using namespace asio;
+using namespace asio::experimental;
+
+void unbuffered_concurrent_channel_test()
+{
+ io_context ctx;
+
+ concurrent_channel<void(asio::error_code, std::string)> ch1(ctx);
+
+ ASIO_CHECK(ch1.is_open());
+ ASIO_CHECK(!ch1.ready());
+
+ bool b1 = ch1.try_send(asio::error::eof, "hello");
+
+ ASIO_CHECK(!b1);
+
+ std::string s1 = "abcdefghijklmnopqrstuvwxyz";
+ bool b2 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(!b2);
+ ASIO_CHECK(!s1.empty());
+
+ asio::error_code ec1;
+ std::string s2;
+ ch1.async_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec1 = ec;
+ s2 = std::move(s);
+ });
+
+ bool b3 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(b3);
+ ASIO_CHECK(s1.empty());
+
+ ctx.run();
+
+ ASIO_CHECK(ec1 == asio::error::eof);
+ ASIO_CHECK(s2 == "abcdefghijklmnopqrstuvwxyz");
+
+ bool b4 = ch1.try_receive([](asio::error_code, std::string){});
+
+ ASIO_CHECK(!b4);
+
+ asio::error_code ec2 = asio::error::would_block;
+ std::string s3 = "zyxwvutsrqponmlkjihgfedcba";
+ ch1.async_send(asio::error::eof, std::move(s3),
+ [&](asio::error_code ec)
+ {
+ ec2 = ec;
+ });
+
+ asio::error_code ec3;
+ std::string s4;
+ bool b5 = ch1.try_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec3 = ec;
+ s4 = s;
+ });
+
+ ASIO_CHECK(b5);
+ ASIO_CHECK(ec3 == asio::error::eof);
+ ASIO_CHECK(s4 == "zyxwvutsrqponmlkjihgfedcba");
+
+ ctx.restart();
+ ctx.run();
+
+ ASIO_CHECK(!ec2);
+};
+
+void buffered_concurrent_channel_test()
+{
+ io_context ctx;
+
+ concurrent_channel<void(asio::error_code, std::string)> ch1(ctx, 1);
+
+ ASIO_CHECK(ch1.is_open());
+ ASIO_CHECK(!ch1.ready());
+
+ bool b1 = ch1.try_send(asio::error::eof, "hello");
+
+ ASIO_CHECK(b1);
+
+ std::string s1 = "abcdefghijklmnopqrstuvwxyz";
+ bool b2 = ch1.try_send(asio::error::eof, std::move(s1));
+
+ ASIO_CHECK(!b2);
+ ASIO_CHECK(!s1.empty());
+
+ asio::error_code ec1;
+ std::string s2;
+ ch1.async_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec1 = ec;
+ s2 = std::move(s);
+ });
+
+ ctx.run();
+
+ ASIO_CHECK(ec1 == asio::error::eof);
+ ASIO_CHECK(s2 == "hello");
+
+ bool b4 = ch1.try_receive([](asio::error_code, std::string){});
+
+ ASIO_CHECK(!b4);
+
+ asio::error_code ec2 = asio::error::would_block;
+ std::string s3 = "zyxwvutsrqponmlkjihgfedcba";
+ ch1.async_send(asio::error::eof, std::move(s3),
+ [&](asio::error_code ec)
+ {
+ ec2 = ec;
+ });
+
+ asio::error_code ec3;
+ std::string s4;
+ bool b5 = ch1.try_receive(
+ [&](asio::error_code ec, std::string s)
+ {
+ ec3 = ec;
+ s4 = s;
+ });
+
+ ASIO_CHECK(b5);
+ ASIO_CHECK(ec3 == asio::error::eof);
+ ASIO_CHECK(s4 == "zyxwvutsrqponmlkjihgfedcba");
+
+ ctx.restart();
+ ctx.run();
+
+ ASIO_CHECK(!ec2);
+};
+
+ASIO_TEST_SUITE
+(
+ "experimental/concurrent_channel",
+ ASIO_TEST_CASE(unbuffered_concurrent_channel_test)
+ ASIO_TEST_CASE(buffered_concurrent_channel_test)
+)