462 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			462 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //
 | ||
|  | // experimental/parallel_group.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_PARALLEL_GROUP_HPP
 | ||
|  | #define ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP
 | ||
|  | 
 | ||
|  | #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | # pragma once
 | ||
|  | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | ||
|  | 
 | ||
|  | #include "asio/detail/config.hpp"
 | ||
|  | #include <vector>
 | ||
|  | #include "asio/detail/array.hpp"
 | ||
|  | #include "asio/detail/memory.hpp"
 | ||
|  | #include "asio/detail/utility.hpp"
 | ||
|  | #include "asio/experimental/cancellation_condition.hpp"
 | ||
|  | 
 | ||
|  | #include "asio/detail/push_options.hpp"
 | ||
|  | 
 | ||
|  | namespace asio { | ||
|  | namespace experimental { | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | // Helper trait for getting a tuple from a completion signature.
 | ||
|  | 
 | ||
|  | template <typename Signature> | ||
|  | struct parallel_op_signature_as_tuple; | ||
|  | 
 | ||
|  | template <typename R, typename... Args> | ||
|  | struct parallel_op_signature_as_tuple<R(Args...)> | ||
|  | { | ||
|  |   typedef std::tuple<typename decay<Args>::type...> type; | ||
|  | }; | ||
|  | 
 | ||
|  | // Helper trait for concatenating completion signatures.
 | ||
|  | 
 | ||
|  | template <std::size_t N, typename Offsets, typename... Signatures> | ||
|  | struct parallel_group_signature; | ||
|  | 
 | ||
|  | template <std::size_t N, typename R0, typename... Args0> | ||
|  | struct parallel_group_signature<N, R0(Args0...)> | ||
|  | { | ||
|  |   typedef asio::detail::array<std::size_t, N> order_type; | ||
|  |   typedef R0 raw_type(Args0...); | ||
|  |   typedef R0 type(order_type, Args0...); | ||
|  | }; | ||
|  | 
 | ||
|  | template <std::size_t N, | ||
|  |     typename R0, typename... Args0, | ||
|  |     typename R1, typename... Args1> | ||
|  | struct parallel_group_signature<N, R0(Args0...), R1(Args1...)> | ||
|  | { | ||
|  |   typedef asio::detail::array<std::size_t, N> order_type; | ||
|  |   typedef R0 raw_type(Args0..., Args1...); | ||
|  |   typedef R0 type(order_type, Args0..., Args1...); | ||
|  | }; | ||
|  | 
 | ||
|  | template <std::size_t N, typename Sig0, | ||
|  |     typename Sig1, typename... SigN> | ||
|  | struct parallel_group_signature<N, Sig0, Sig1, SigN...> | ||
|  | { | ||
|  |   typedef asio::detail::array<std::size_t, N> order_type; | ||
|  |   typedef typename parallel_group_signature<N, | ||
|  |     typename parallel_group_signature<N, Sig0, Sig1>::raw_type, | ||
|  |       SigN...>::raw_type raw_type; | ||
|  |   typedef typename parallel_group_signature<N, | ||
|  |     typename parallel_group_signature<N, Sig0, Sig1>::raw_type, | ||
|  |       SigN...>::type type; | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Condition, typename Handler, | ||
|  |     typename... Ops, std::size_t... I> | ||
|  | void parallel_group_launch(Condition cancellation_condition, Handler handler, | ||
|  |     std::tuple<Ops...>& ops, asio::detail::index_sequence<I...>); | ||
|  | 
 | ||
|  | // Helper trait for determining ranged parallel group completion signatures.
 | ||
|  | 
 | ||
|  | template <typename Signature, typename Allocator> | ||
|  | struct ranged_parallel_group_signature; | ||
|  | 
 | ||
|  | template <typename R, typename... Args, typename Allocator> | ||
|  | struct ranged_parallel_group_signature<R(Args...), Allocator> | ||
|  | { | ||
|  |   typedef std::vector<std::size_t, | ||
|  |     ASIO_REBIND_ALLOC(Allocator, std::size_t)> order_type; | ||
|  |   typedef R raw_type( | ||
|  |       std::vector<Args, ASIO_REBIND_ALLOC(Allocator, Args)>...); | ||
|  |   typedef R type(order_type, | ||
|  |       std::vector<Args, ASIO_REBIND_ALLOC(Allocator, Args)>...); | ||
|  | }; | ||
|  | 
 | ||
|  | template <typename Condition, typename Handler, | ||
|  |     typename Range, typename Allocator> | ||
|  | void ranged_parallel_group_launch(Condition cancellation_condition, | ||
|  |     Handler handler, Range&& range, const Allocator& allocator); | ||
|  | 
 | ||
|  | char (¶llel_group_has_iterator_helper(...))[2]; | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | char parallel_group_has_iterator_helper(T*, typename T::iterator* = 0); | ||
|  | 
 | ||
|  | template <typename T> | ||
|  | struct parallel_group_has_iterator_typedef | ||
|  | { | ||
|  |   enum { value = (sizeof((parallel_group_has_iterator_helper)((T*)(0))) == 1) }; | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace detail
 | ||
|  | 
 | ||
|  | /// Type trait used to determine whether a type is a range of asynchronous
 | ||
|  | /// operations that can be used with with @c make_parallel_group.
 | ||
|  | template <typename T> | ||
|  | struct is_async_operation_range | ||
|  | { | ||
|  | #if defined(GENERATING_DOCUMENTATION)
 | ||
|  |   /// The value member is true if the type may be used as a range of
 | ||
|  |   /// asynchronous operations.
 | ||
|  |   static const bool value; | ||
|  | #else
 | ||
|  |   enum | ||
|  |   { | ||
|  |     value = detail::parallel_group_has_iterator_typedef<T>::value | ||
|  |   }; | ||
|  | #endif
 | ||
|  | }; | ||
|  | 
 | ||
|  | /// A group of asynchronous operations that may be launched in parallel.
 | ||
|  | /**
 | ||
|  |  * See the documentation for asio::experimental::make_parallel_group for | ||
|  |  * a usage example. | ||
|  |  */ | ||
|  | template <typename... Ops> | ||
|  | class parallel_group | ||
|  | { | ||
|  | private: | ||
|  |   struct initiate_async_wait | ||
|  |   { | ||
|  |     template <typename Handler, typename Condition> | ||
|  |     void operator()(Handler&& h, Condition&& c, std::tuple<Ops...>&& ops) const | ||
|  |     { | ||
|  |       detail::parallel_group_launch( | ||
|  |           std::forward<Condition>(c), std::forward<Handler>(h), | ||
|  |           ops, asio::detail::index_sequence_for<Ops...>()); | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   std::tuple<Ops...> ops_; | ||
|  | 
 | ||
|  | public: | ||
|  |   /// Constructor.
 | ||
|  |   explicit parallel_group(Ops... ops) | ||
|  |     : ops_(std::move(ops)...) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   /// The completion signature for the group of operations.
 | ||
|  |   typedef typename detail::parallel_group_signature<sizeof...(Ops), | ||
|  |       typename completion_signature_of<Ops>::type...>::type signature; | ||
|  | 
 | ||
|  |   /// Initiate an asynchronous wait for the group of operations.
 | ||
|  |   /**
 | ||
|  |    * Launches the group and asynchronously waits for completion. | ||
|  |    * | ||
|  |    * @param cancellation_condition A function object, called on completion of | ||
|  |    * an operation within the group, that is used to determine whether to cancel | ||
|  |    * the remaining operations. The function object is passed the arguments of | ||
|  |    * the completed operation's handler. To trigger cancellation of the remaining | ||
|  |    * operations, it must return a asio::cancellation_type value other | ||
|  |    * than <tt>asio::cancellation_type::none</tt>. | ||
|  |    * | ||
|  |    * @param token A @ref completion_token whose signature is comprised of | ||
|  |    * a @c std::array<std::size_t, N> indicating the completion order of the | ||
|  |    * operations, followed by all operations' completion handler arguments. | ||
|  |    * | ||
|  |    * The library provides the following @c cancellation_condition types: | ||
|  |    * | ||
|  |    * @li asio::experimental::wait_for_all | ||
|  |    * @li asio::experimental::wait_for_one | ||
|  |    * @li asio::experimental::wait_for_one_error | ||
|  |    * @li asio::experimental::wait_for_one_success | ||
|  |    */ | ||
|  |   template <typename CancellationCondition, | ||
|  |       ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken> | ||
|  |   ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken, signature) | ||
|  |   async_wait(CancellationCondition cancellation_condition, | ||
|  |       CompletionToken&& token) | ||
|  |     ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( | ||
|  |       asio::async_initiate<CompletionToken, signature>( | ||
|  |           declval<initiate_async_wait>(), token, | ||
|  |           std::move(cancellation_condition), std::move(ops_)))) | ||
|  |   { | ||
|  |     return asio::async_initiate<CompletionToken, signature>( | ||
|  |         initiate_async_wait(), token, | ||
|  |         std::move(cancellation_condition), std::move(ops_)); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | /// Create a group of operations that may be launched in parallel.
 | ||
|  | /**
 | ||
|  |  * For example: | ||
|  |  * @code asio::experimental::make_parallel_group( | ||
|  |  *    [&](auto token) | ||
|  |  *    { | ||
|  |  *      return in.async_read_some(asio::buffer(data), token); | ||
|  |  *    }, | ||
|  |  *    [&](auto token) | ||
|  |  *    { | ||
|  |  *      return timer.async_wait(token); | ||
|  |  *    } | ||
|  |  *  ).async_wait( | ||
|  |  *    asio::experimental::wait_for_all(), | ||
|  |  *    []( | ||
|  |  *        std::array<std::size_t, 2> completion_order, | ||
|  |  *        std::error_code ec1, std::size_t n1, | ||
|  |  *        std::error_code ec2 | ||
|  |  *    ) | ||
|  |  *    { | ||
|  |  *      switch (completion_order[0]) | ||
|  |  *      { | ||
|  |  *      case 0: | ||
|  |  *        { | ||
|  |  *          std::cout << "descriptor finished: " << ec1 << ", " << n1 << "\n"; | ||
|  |  *        } | ||
|  |  *        break; | ||
|  |  *      case 1: | ||
|  |  *        { | ||
|  |  *          std::cout << "timer finished: " << ec2 << "\n"; | ||
|  |  *        } | ||
|  |  *        break; | ||
|  |  *      } | ||
|  |  *    } | ||
|  |  *  ); | ||
|  |  * @endcode | ||
|  |  */ | ||
|  | template <typename... Ops> | ||
|  | ASIO_NODISCARD inline parallel_group<Ops...> | ||
|  | make_parallel_group(Ops... ops) | ||
|  | { | ||
|  |   return parallel_group<Ops...>(std::move(ops)...); | ||
|  | } | ||
|  | 
 | ||
|  | /// A range-based group of asynchronous operations that may be launched in
 | ||
|  | /// parallel.
 | ||
|  | /**
 | ||
|  |  * See the documentation for asio::experimental::make_parallel_group for | ||
|  |  * a usage example. | ||
|  |  */ | ||
|  | template <typename Range, typename Allocator = std::allocator<void> > | ||
|  | class ranged_parallel_group | ||
|  | { | ||
|  | private: | ||
|  |   struct initiate_async_wait | ||
|  |   { | ||
|  |     template <typename Handler, typename Condition> | ||
|  |     void operator()(Handler&& h, Condition&& c, | ||
|  |         Range&& range, const Allocator& allocator) const | ||
|  |     { | ||
|  |       detail::ranged_parallel_group_launch(std::move(c), | ||
|  |           std::move(h), std::forward<Range>(range), allocator); | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   Range range_; | ||
|  |   Allocator allocator_; | ||
|  | 
 | ||
|  | public: | ||
|  |   /// Constructor.
 | ||
|  |   explicit ranged_parallel_group(Range range, | ||
|  |       const Allocator& allocator = Allocator()) | ||
|  |     : range_(std::move(range)), | ||
|  |       allocator_(allocator) | ||
|  |   { | ||
|  |   } | ||
|  | 
 | ||
|  |   /// The completion signature for the group of operations.
 | ||
|  |   typedef typename detail::ranged_parallel_group_signature< | ||
|  |       typename completion_signature_of< | ||
|  |         typename std::decay< | ||
|  |           decltype(*std::declval<typename Range::iterator>())>::type>::type, | ||
|  |       Allocator>::type signature; | ||
|  | 
 | ||
|  |   /// Initiate an asynchronous wait for the group of operations.
 | ||
|  |   /**
 | ||
|  |    * Launches the group and asynchronously waits for completion. | ||
|  |    * | ||
|  |    * @param cancellation_condition A function object, called on completion of | ||
|  |    * an operation within the group, that is used to determine whether to cancel | ||
|  |    * the remaining operations. The function object is passed the arguments of | ||
|  |    * the completed operation's handler. To trigger cancellation of the remaining | ||
|  |    * operations, it must return a asio::cancellation_type value other | ||
|  |    * than <tt>asio::cancellation_type::none</tt>. | ||
|  |    * | ||
|  |    * @param token A @ref completion_token whose signature is comprised of | ||
|  |    * a @c std::vector<std::size_t, Allocator> indicating the completion order of | ||
|  |    * the operations, followed by a vector for each of the completion signature's | ||
|  |    * arguments. | ||
|  |    * | ||
|  |    * The library provides the following @c cancellation_condition types: | ||
|  |    * | ||
|  |    * @li asio::experimental::wait_for_all | ||
|  |    * @li asio::experimental::wait_for_one | ||
|  |    * @li asio::experimental::wait_for_one_error | ||
|  |    * @li asio::experimental::wait_for_one_success | ||
|  |    */ | ||
|  |   template <typename CancellationCondition, | ||
|  |       ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken> | ||
|  |   ASIO_INITFN_AUTO_RESULT_TYPE_PREFIX(CompletionToken, signature) | ||
|  |   async_wait(CancellationCondition cancellation_condition, | ||
|  |       CompletionToken&& token) | ||
|  |     ASIO_INITFN_AUTO_RESULT_TYPE_SUFFIX(( | ||
|  |       asio::async_initiate<CompletionToken, signature>( | ||
|  |           declval<initiate_async_wait>(), token, | ||
|  |           std::move(cancellation_condition), | ||
|  |           std::move(range_), allocator_))) | ||
|  |   { | ||
|  |     return asio::async_initiate<CompletionToken, signature>( | ||
|  |         initiate_async_wait(), token, | ||
|  |         std::move(cancellation_condition), | ||
|  |         std::move(range_), allocator_); | ||
|  |   } | ||
|  | }; | ||
|  | 
 | ||
|  | /// Create a group of operations that may be launched in parallel.
 | ||
|  | /**
 | ||
|  |  * @param range A range containing the operations to be launched. | ||
|  |  * | ||
|  |  * For example: | ||
|  |  * @code | ||
|  |  * using op_type = decltype( | ||
|  |  *     socket1.async_read_some( | ||
|  |  *       asio::buffer(data1), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * std::vector<op_type> ops; | ||
|  |  * | ||
|  |  * ops.push_back( | ||
|  |  *     socket1.async_read_some( | ||
|  |  *       asio::buffer(data1), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * ops.push_back( | ||
|  |  *     socket2.async_read_some( | ||
|  |  *       asio::buffer(data2), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * asio::experimental::make_parallel_group(ops).async_wait( | ||
|  |  *     asio::experimental::wait_for_all(), | ||
|  |  *     []( | ||
|  |  *         std::vector<std::size_t> completion_order, | ||
|  |  *         std::vector<std::error_code> e, | ||
|  |  *         std::vector<std::size_t> n | ||
|  |  *       ) | ||
|  |  *     { | ||
|  |  *       for (std::size_t i = 0; i < completion_order.size(); ++i) | ||
|  |  *       { | ||
|  |  *         std::size_t idx = completion_order[i]; | ||
|  |  *         std::cout << "socket " << idx << " finished: "; | ||
|  |  *         std::cout << e[idx] << ", " << n[idx] << "\n"; | ||
|  |  *       } | ||
|  |  *     } | ||
|  |  *   ); | ||
|  |  * @endcode | ||
|  |  */ | ||
|  | template <typename Range> | ||
|  | ASIO_NODISCARD inline | ||
|  | ranged_parallel_group<typename std::decay<Range>::type> | ||
|  | make_parallel_group(Range&& range, | ||
|  |     typename constraint< | ||
|  |       is_async_operation_range<typename std::decay<Range>::type>::value | ||
|  |     >::type = 0) | ||
|  | { | ||
|  |   return ranged_parallel_group<typename std::decay<Range>::type>( | ||
|  |       std::forward<Range>(range)); | ||
|  | } | ||
|  | 
 | ||
|  | /// Create a group of operations that may be launched in parallel.
 | ||
|  | /**
 | ||
|  |  * @param allocator Specifies the allocator to be used with the result vectors. | ||
|  |  * | ||
|  |  * @param range A range containing the operations to be launched. | ||
|  |  * | ||
|  |  * For example: | ||
|  |  * @code | ||
|  |  * using op_type = decltype( | ||
|  |  *     socket1.async_read_some( | ||
|  |  *       asio::buffer(data1), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * std::vector<op_type> ops; | ||
|  |  * | ||
|  |  * ops.push_back( | ||
|  |  *     socket1.async_read_some( | ||
|  |  *       asio::buffer(data1), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * ops.push_back( | ||
|  |  *     socket2.async_read_some( | ||
|  |  *       asio::buffer(data2), | ||
|  |  *       asio::deferred | ||
|  |  *     ) | ||
|  |  *   ); | ||
|  |  * | ||
|  |  * asio::experimental::make_parallel_group( | ||
|  |  *     std::allocator_arg_t, | ||
|  |  *     my_allocator, | ||
|  |  *     ops | ||
|  |  *   ).async_wait( | ||
|  |  *     asio::experimental::wait_for_all(), | ||
|  |  *     []( | ||
|  |  *         std::vector<std::size_t> completion_order, | ||
|  |  *         std::vector<std::error_code> e, | ||
|  |  *         std::vector<std::size_t> n | ||
|  |  *       ) | ||
|  |  *     { | ||
|  |  *       for (std::size_t i = 0; i < completion_order.size(); ++i) | ||
|  |  *       { | ||
|  |  *         std::size_t idx = completion_order[i]; | ||
|  |  *         std::cout << "socket " << idx << " finished: "; | ||
|  |  *         std::cout << e[idx] << ", " << n[idx] << "\n"; | ||
|  |  *       } | ||
|  |  *     } | ||
|  |  *   ); | ||
|  |  * @endcode | ||
|  |  */ | ||
|  | template <typename Allocator, typename Range> | ||
|  | ASIO_NODISCARD inline | ||
|  | ranged_parallel_group<typename std::decay<Range>::type, Allocator> | ||
|  | make_parallel_group(allocator_arg_t, const Allocator& allocator, Range&& range, | ||
|  |     typename constraint< | ||
|  |       is_async_operation_range<typename std::decay<Range>::type>::value | ||
|  |     >::type = 0) | ||
|  | { | ||
|  |   return ranged_parallel_group<typename std::decay<Range>::type, Allocator>( | ||
|  |       std::forward<Range>(range), allocator); | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace experimental
 | ||
|  | } // namespace asio
 | ||
|  | 
 | ||
|  | #include "asio/detail/pop_options.hpp"
 | ||
|  | 
 | ||
|  | #include "asio/experimental/impl/parallel_group.hpp"
 | ||
|  | 
 | ||
|  | #endif // ASIO_EXPERIMENTAL_PARALLEL_GROUP_HPP
 |