678 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			678 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //
 | ||
|  | // experimental/detail/channel_service.hpp
 | ||
|  | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | ||
|  | //
 | ||
|  | // Copyright (c) 2003-2023 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 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_; | ||
|  | }; | ||
|  | 
 | ||
|  | // The implementation for an error_code signature.
 | ||
|  | template <typename Mutex> | ||
|  | template <typename Traits, typename R> | ||
|  | struct channel_service<Mutex>::implementation_type< | ||
|  |     Traits, R(asio::error_code)> | ||
|  |   : channel_service::base_implementation_type | ||
|  | { | ||
|  |   // The traits type associated with the channel.
 | ||
|  |   typedef typename Traits::template rebind<R(asio::error_code)>::other | ||
|  |     traits_type; | ||
|  | 
 | ||
|  |   // Type of an element stored in the buffer.
 | ||
|  |   typedef typename conditional< | ||
|  |       has_signature< | ||
|  |         typename traits_type::receive_cancelled_signature, | ||
|  |         R(asio::error_code) | ||
|  |       >::value, | ||
|  |       typename conditional< | ||
|  |         has_signature< | ||
|  |           typename traits_type::receive_closed_signature, | ||
|  |           R(asio::error_code) | ||
|  |         >::value, | ||
|  |         channel_payload<R(asio::error_code)>, | ||
|  |         channel_payload< | ||
|  |           R(asio::error_code), | ||
|  |           typename traits_type::receive_closed_signature | ||
|  |         > | ||
|  |       >::type, | ||
|  |       typename conditional< | ||
|  |         has_signature< | ||
|  |           typename traits_type::receive_closed_signature, | ||
|  |           R(asio::error_code), | ||
|  |           typename traits_type::receive_cancelled_signature | ||
|  |         >::value, | ||
|  |         channel_payload< | ||
|  |           R(asio::error_code), | ||
|  |           typename traits_type::receive_cancelled_signature | ||
|  |         >, | ||
|  |         channel_payload< | ||
|  |           R(asio::error_code), | ||
|  |           typename traits_type::receive_cancelled_signature, | ||
|  |           typename traits_type::receive_closed_signature | ||
|  |         > | ||
|  |       >::type | ||
|  |     >::type payload_type; | ||
|  | 
 | ||
|  |   // Construct with empty buffer.
 | ||
|  |   implementation_type() | ||
|  |     : size_(0) | ||
|  |   { | ||
|  |     first_.count_ = 0; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Move from another buffer.
 | ||
|  |   void buffer_move_from(implementation_type& other) | ||
|  |   { | ||
|  |     size_ = other.buffer_; | ||
|  |     other.size_ = 0; | ||
|  |     first_ = other.first_; | ||
|  |     other.first.count_ = 0; | ||
|  |     rest_ = ASIO_MOVE_CAST( | ||
|  |         typename traits_type::template container< | ||
|  |           buffered_value>::type)(other.rest_); | ||
|  |     other.buffer_clear(); | ||
|  |   } | ||
|  | 
 | ||
|  |   // Get number of buffered elements.
 | ||
|  |   std::size_t buffer_size() const | ||
|  |   { | ||
|  |     return size_; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Push a new value to the back of the buffer.
 | ||
|  |   void buffer_push(payload_type payload) | ||
|  |   { | ||
|  |     buffered_value& last = rest_.empty() ? first_ : rest_.back(); | ||
|  |     if (last.count_ == 0) | ||
|  |     { | ||
|  |       value_handler handler{last.value_}; | ||
|  |       payload.receive(handler); | ||
|  |       last.count_ = 1; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |       asio::error_code value{last.value_}; | ||
|  |       value_handler handler{value}; | ||
|  |       payload.receive(handler); | ||
|  |       if (last.value_ == value) | ||
|  |         ++last.count_; | ||
|  |       else | ||
|  |         rest_.push_back({value, 1}); | ||
|  |     } | ||
|  |     ++size_; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Push new values to the back of the buffer.
 | ||
|  |   std::size_t buffer_push_n(std::size_t count, payload_type payload) | ||
|  |   { | ||
|  |     std::size_t available = this->max_buffer_size_ - size_; | ||
|  |     count = (count < available) ? count : available; | ||
|  |     if (count > 0) | ||
|  |     { | ||
|  |       buffered_value& last = rest_.empty() ? first_ : rest_.back(); | ||
|  |       if (last.count_ == 0) | ||
|  |       { | ||
|  |         payload.receive(value_handler{last.value_}); | ||
|  |         last.count_ = count; | ||
|  |       } | ||
|  |       else | ||
|  |       { | ||
|  |         asio::error_code value{last.value_}; | ||
|  |         payload.receive(value_handler{value}); | ||
|  |         if (last.value_ == value) | ||
|  |           last.count_ += count; | ||
|  |         else | ||
|  |           rest_.push_back({value, count}); | ||
|  |       } | ||
|  |       size_ += count; | ||
|  |     } | ||
|  |     return count; | ||
|  |   } | ||
|  | 
 | ||
|  |   // Get the element at the front of the buffer.
 | ||
|  |   payload_type buffer_front() | ||
|  |   { | ||
|  |     return payload_type({0, first_.value_}); | ||
|  |   } | ||
|  | 
 | ||
|  |   // Pop a value from the front of the buffer.
 | ||
|  |   void buffer_pop() | ||
|  |   { | ||
|  |     --size_; | ||
|  |     if (--first_.count_ == 0 && !rest_.empty()) | ||
|  |     { | ||
|  |       first_ = rest_.front(); | ||
|  |       rest_.pop_front(); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // Clear all values from the buffer.
 | ||
|  |   void buffer_clear() | ||
|  |   { | ||
|  |     size_ = 0; | ||
|  |     first_.count_ == 0; | ||
|  |     rest_.clear(); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   struct buffered_value | ||
|  |   { | ||
|  |     asio::error_code value_; | ||
|  |     std::size_t count_; | ||
|  |   }; | ||
|  | 
 | ||
|  |   struct value_handler | ||
|  |   { | ||
|  |     asio::error_code& target_; | ||
|  | 
 | ||
|  |     template <typename... Args> | ||
|  |     void operator()(const asio::error_code& value, Args&&...) | ||
|  |     { | ||
|  |       target_ = value; | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   buffered_value& last_value() | ||
|  |   { | ||
|  |     return rest_.empty() ? first_ : rest_.back(); | ||
|  |   } | ||
|  | 
 | ||
|  |   // Total number of buffered values.
 | ||
|  |   std::size_t size_; | ||
|  | 
 | ||
|  |   // The first buffered value is maintained as a separate data member to avoid
 | ||
|  |   // allocating space in the container in the common case.
 | ||
|  |   buffered_value first_; | ||
|  | 
 | ||
|  |   // The rest of the buffered values.
 | ||
|  |   typename traits_type::template container<buffered_value>::type rest_; | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace detail
 | ||
|  | } // namespace experimental
 | ||
|  | } // namespace asio
 | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp"
 | ||
|  | 
 | ||
|  | #include "asio/experimental/detail/impl/channel_service.hpp"
 | ||
|  | 
 | ||
|  | #endif // ASIO_EXPERIMENTAL_DETAIL_CHANNEL_SERVICE_HPP
 |