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);
+  ``  [''''&raquo;''' [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());
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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());
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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);
+  ``  [''''&raquo;''' [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)
+)