Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:27

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
0008 //
0009 
0010 #ifndef BOOST_BEAST_CORE_ASYNC_BASE_HPP
0011 #define BOOST_BEAST_CORE_ASYNC_BASE_HPP
0012 
0013 #include <boost/beast/core/detail/config.hpp>
0014 #include <boost/beast/core/bind_handler.hpp>
0015 #include <boost/beast/core/detail/allocator.hpp>
0016 #include <boost/beast/core/detail/async_base.hpp>
0017 #include <boost/beast/core/detail/filtering_cancellation_slot.hpp>
0018 #include <boost/beast/core/detail/work_guard.hpp>
0019 #include <boost/asio/associated_allocator.hpp>
0020 #include <boost/asio/associated_executor.hpp>
0021 #include <boost/asio/associated_immediate_executor.hpp>
0022 #include <boost/asio/bind_executor.hpp>
0023 #include <boost/asio/handler_continuation_hook.hpp>
0024 #include <boost/asio/dispatch.hpp>
0025 #include <boost/asio/post.hpp>
0026 #include <boost/core/exchange.hpp>
0027 #include <boost/core/empty_value.hpp>
0028 #include <utility>
0029 
0030 namespace boost {
0031 namespace beast {
0032 
0033 
0034 /** Base class to assist writing composed operations.
0035 
0036     A function object submitted to intermediate initiating functions during
0037     a composed operation may derive from this type to inherit all of the
0038     boilerplate to forward the executor, allocator, and legacy customization
0039     points associated with the completion handler invoked at the end of the
0040     composed operation.
0041 
0042     The composed operation must be typical; that is, associated with one
0043     executor of an I/O object, and invoking a caller-provided completion
0044     handler when the operation is finished. Classes derived from
0045     @ref async_base will acquire these properties:
0046 
0047     @li Ownership of the final completion handler provided upon construction.
0048 
0049     @li If the final handler has an associated allocator, this allocator will
0050         be propagated to the composed operation subclass. Otherwise, the
0051         associated allocator will be the type specified in the allocator
0052         template parameter, or the default of `std::allocator<void>` if the
0053         parameter is omitted.
0054 
0055     @li If the final handler has an associated executor, then it will be used
0056         as the executor associated with the composed operation. Otherwise,
0057         the specified `Executor1` will be the type of executor associated
0058         with the composed operation.
0059 
0060     @li An instance of `net::executor_work_guard` for the instance of `Executor1`
0061         shall be maintained until either the final handler is invoked, or the
0062         operation base is destroyed, whichever comes first.
0063 
0064     @li Calls to the legacy customization point `asio_handler_is_continuation`
0065         which use argument-dependent lookup, will be forwarded to the
0066         legacy customization points associated with the handler.
0067 
0068     @par Example
0069 
0070     The following code demonstrates how @ref async_base may be be used to
0071     assist authoring an asynchronous initiating function, by providing all of
0072     the boilerplate to manage the final completion handler in a way that
0073     maintains the allocator and executor associations:
0074 
0075     @code
0076 
0077     // Asynchronously read into a buffer until the buffer is full, or an error occurs
0078     template<class AsyncReadStream, class ReadHandler>
0079     typename net::async_result<ReadHandler, void(error_code, std::size_t)>::return_type
0080     async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
0081     {
0082         using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
0083         using base_type = async_base<handler_type, typename AsyncReadStream::executor_type>;
0084 
0085         struct op : base_type
0086         {
0087             AsyncReadStream& stream_;
0088             net::mutable_buffer buffer_;
0089             std::size_t total_bytes_transferred_;
0090 
0091             op(
0092                 AsyncReadStream& stream,
0093                 net::mutable_buffer buffer,
0094                 handler_type& handler)
0095                 : base_type(std::move(handler), stream.get_executor())
0096                 , stream_(stream)
0097                 , buffer_(buffer)
0098                 , total_bytes_transferred_(0)
0099             {
0100                 (*this)({}, 0, false); // start the operation
0101             }
0102 
0103             void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
0104             {
0105                 // Adjust the count of bytes and advance our buffer
0106                 total_bytes_transferred_ += bytes_transferred;
0107                 buffer_ = buffer_ + bytes_transferred;
0108 
0109                 // Keep reading until buffer is full or an error occurs
0110                 if(! ec && buffer_.size() > 0)
0111                     return stream_.async_read_some(buffer_, std::move(*this));
0112 
0113                 // Call the completion handler with the result. If `is_continuation` is
0114                 // false, which happens on the first time through this function, then
0115                 // `net::post` will be used to call the completion handler, otherwise
0116                 // the completion handler will be invoked directly.
0117 
0118                 this->complete(is_continuation, ec, total_bytes_transferred_);
0119             }
0120         };
0121 
0122         net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
0123         op(stream, buffer, init.completion_handler);
0124         return init.result.get();
0125     }
0126 
0127     @endcode
0128 
0129     Data members of composed operations implemented as completion handlers
0130     do not have stable addresses, as the composed operation object is move
0131     constructed upon each call to an initiating function. For most operations
0132     this is not a problem. For complex operations requiring stable temporary
0133     storage, the class @ref stable_async_base is provided which offers
0134     additional functionality:
0135 
0136     @li The free function @ref allocate_stable may be used to allocate
0137     one or more temporary objects associated with the composed operation.
0138 
0139     @li Memory for stable temporary objects is allocated using the allocator
0140     associated with the composed operation.
0141 
0142     @li Stable temporary objects are automatically destroyed, and the memory
0143     freed using the associated allocator, either before the final completion
0144     handler is invoked (a Networking requirement) or when the composed operation
0145     is destroyed, whichever occurs first.
0146 
0147     @par Temporary Storage Example
0148 
0149     The following example demonstrates how a composed operation may store a
0150     temporary object.
0151 
0152     @code
0153 
0154     @endcode
0155 
0156     @tparam Handler The type of the completion handler to store.
0157     This type must meet the requirements of <em>CompletionHandler</em>.
0158 
0159     @tparam Executor1 The type of the executor used when the handler has no
0160     associated executor. An instance of this type must be provided upon
0161     construction. The implementation will maintain an executor work guard
0162     and a copy of this instance.
0163 
0164     @tparam Allocator The allocator type to use if the handler does not
0165     have an associated allocator. If this parameter is omitted, then
0166     `std::allocator<void>` will be used. If the specified allocator is
0167     not default constructible, an instance of the type must be provided
0168     upon construction.
0169 
0170     @see stable_async_base
0171 */
0172 template<
0173     class Handler,
0174     class Executor1,
0175     class Allocator = std::allocator<void>
0176 >
0177 class async_base
0178 #if ! BOOST_BEAST_DOXYGEN
0179     : private boost::empty_value<Allocator>
0180 #endif
0181 {
0182     static_assert(
0183         net::is_executor<Executor1>::value || net::execution::is_executor<Executor1>::value,
0184         "Executor type requirements not met");
0185 
0186     Handler h_;
0187     detail::select_work_guard_t<Executor1> wg1_;
0188     net::cancellation_type act_{net::cancellation_type::terminal};
0189 public:
0190     /** The type of executor associated with this object.
0191 
0192     If a class derived from @ref boost::beast::async_base is a completion
0193     handler, then the associated executor of the derived class will
0194     be this type.
0195 */
0196     using executor_type =
0197 #if BOOST_BEAST_DOXYGEN
0198         __implementation_defined__;
0199 #else
0200         typename
0201         net::associated_executor<
0202             Handler,
0203             typename detail::select_work_guard_t<Executor1>::executor_type
0204                 >::type;
0205 #endif
0206 
0207     /** The type of the immediate executor associated with this object.
0208 
0209     If a class derived from @ref boost::beast::async_base is a completion
0210     handler, then the associated immediage executor of the derived class will
0211     be this type.
0212 */
0213     using immediate_executor_type =
0214 #if BOOST_BEAST_DOXYGEN
0215         __implementation_defined__;
0216 #else
0217         typename
0218         net::associated_immediate_executor<
0219             Handler,
0220             typename detail::select_work_guard_t<Executor1>::executor_type
0221                 >::type;
0222 #endif
0223 
0224 
0225   private:
0226 
0227     virtual
0228     void
0229     before_invoke_hook()
0230     {
0231     }
0232 
0233 public:
0234     /** Constructor
0235 
0236         @param handler The final completion handler.
0237         The type of this object must meet the requirements of <em>CompletionHandler</em>.
0238         The implementation takes ownership of the handler by performing a decay-copy.
0239 
0240         @param ex1 The executor associated with the implied I/O object
0241         target of the operation. The implementation shall maintain an
0242         executor work guard for the lifetime of the operation, or until
0243         the final completion handler is invoked, whichever is shorter.
0244 
0245         @param alloc The allocator to be associated with objects
0246         derived from this class. If `Allocator` is default-constructible,
0247         this parameter is optional and may be omitted.
0248     */
0249 #if BOOST_BEAST_DOXYGEN
0250     template<class Handler_>
0251     async_base(
0252         Handler&& handler,
0253         Executor1 const& ex1,
0254         Allocator const& alloc = Allocator());
0255 #else
0256     template<
0257         class Handler_,
0258         class = typename std::enable_if<
0259             ! std::is_same<typename
0260                 std::decay<Handler_>::type,
0261                 async_base
0262             >::value>::type
0263     >
0264     async_base(
0265         Handler_&& handler,
0266         Executor1 const& ex1)
0267         : h_(std::forward<Handler_>(handler))
0268         , wg1_(detail::make_work_guard(ex1))
0269     {
0270     }
0271 
0272     template<class Handler_>
0273     async_base(
0274         Handler_&& handler,
0275         Executor1 const& ex1,
0276         Allocator const& alloc)
0277         : boost::empty_value<Allocator>(
0278             boost::empty_init_t{}, alloc)
0279         , h_(std::forward<Handler_>(handler))
0280         , wg1_(ex1)
0281     {
0282     }
0283 #endif
0284 
0285     /// Move Constructor
0286     async_base(async_base&& other) = default;
0287 
0288     virtual ~async_base() = default;
0289     async_base(async_base const&) = delete;
0290     async_base& operator=(async_base const&) = delete;
0291 
0292     /** The type of allocator associated with this object.
0293 
0294         If a class derived from @ref boost::beast::async_base is a completion
0295         handler, then the associated allocator of the derived class will
0296         be this type.
0297     */
0298     using allocator_type =
0299         net::associated_allocator_t<Handler, Allocator>;
0300 
0301     /** Returns the allocator associated with this object.
0302 
0303         If a class derived from @ref boost::beast::async_base is a completion
0304         handler, then the object returned from this function will be used
0305         as the associated allocator of the derived class.
0306     */
0307     allocator_type
0308     get_allocator() const noexcept
0309     {
0310         return net::get_associated_allocator(h_,
0311             boost::empty_value<Allocator>::get());
0312     }
0313 
0314     /** Returns the executor associated with this object.
0315 
0316         If a class derived from @ref boost::beast::async_base is a completion
0317         handler, then the object returned from this function will be used
0318         as the associated executor of the derived class.
0319     */
0320     executor_type
0321     get_executor() const noexcept
0322     {
0323         return net::get_associated_executor(
0324             h_, wg1_.get_executor());
0325     }
0326 
0327     /** Returns the immediate executor associated with this handler.
0328         If the handler has none it returns asios default immediate
0329         executor based on the executor of the object.
0330 
0331         If a class derived from @ref boost::beast::async_base is a completion
0332         handler, then the object returned from this function will be used
0333         as the associated immediate executor of the derived class.
0334     */
0335     immediate_executor_type
0336     get_immediate_executor() const noexcept
0337     {
0338         return net::get_associated_immediate_executor(
0339             h_, wg1_.get_executor());
0340     }
0341 
0342 
0343   /** The type of cancellation_slot associated with this object.
0344 
0345       If a class derived from @ref async_base is a completion
0346       handler, then the associated cancellation_slot of the
0347       derived class will be this type.
0348 
0349       The default type is a filtering cancellation slot,
0350       that only allows terminal cancellation.
0351   */
0352     using cancellation_slot_type =
0353             beast::detail::filtering_cancellation_slot<net::associated_cancellation_slot_t<Handler>>;
0354 
0355     /** Returns the cancellation_slot associated with this object.
0356 
0357         If a class derived from @ref async_base is a completion
0358         handler, then the object returned from this function will be used
0359         as the associated cancellation_slot of the derived class.
0360     */
0361     cancellation_slot_type
0362     get_cancellation_slot() const noexcept
0363     {
0364         return cancellation_slot_type(act_, net::get_associated_cancellation_slot(h_,
0365             net::cancellation_slot()));
0366     }
0367 
0368     /// Set the allowed cancellation types, default is `terminal`.
0369     void set_allowed_cancellation(
0370             net::cancellation_type allowed_cancellation_types = net::cancellation_type::terminal)
0371     {
0372         act_ = allowed_cancellation_types;
0373     }
0374 
0375     /// Returns the handler associated with this object
0376     Handler const&
0377     handler() const noexcept
0378     {
0379         return h_;
0380     }
0381 
0382     /** Returns ownership of the handler associated with this object
0383 
0384         This function is used to transfer ownership of the handler to
0385         the caller, by move-construction. After the move, the only
0386         valid operations on the base object are move construction and
0387         destruction.
0388     */
0389     Handler
0390     release_handler()
0391     {
0392         return std::move(h_);
0393     }
0394 
0395     /** Invoke the final completion handler, maybe using post.
0396 
0397         This invokes the final completion handler with the specified
0398         arguments forwarded. It is undefined to call either of
0399         @ref boost::beast::async_base::complete or
0400         @ref boost::beast::async_base::complete_now more than once.
0401 
0402         Any temporary objects allocated with @ref boost::beast::allocate_stable will
0403         be automatically destroyed before the final completion handler
0404         is invoked.
0405 
0406         @param is_continuation If this value is `false`, then the
0407         handler will be submitted to the to the immediate executor using
0408         `net::dispatch`. If the handler has no immediate executor,
0409         this will submit to the executor via `net::post`.
0410         Otherwise the handler will be invoked as if by calling
0411         @ref boost::beast::async_base::complete_now.
0412 
0413         @param args A list of optional parameters to invoke the handler
0414         with. The completion handler must be invocable with the parameter
0415         list, or else a compilation error will result.
0416     */
0417     template<class... Args>
0418     void
0419     complete(bool is_continuation, Args&&... args)
0420     {
0421         this->before_invoke_hook();
0422         if(! is_continuation)
0423         {
0424             auto const ex = this->get_immediate_executor();
0425             net::dispatch(
0426                 ex,
0427                 beast::bind_front_handler(
0428                     std::move(h_),
0429                     std::forward<Args>(args)...));
0430             wg1_.reset();
0431         }
0432         else
0433         {
0434             wg1_.reset();
0435             h_(std::forward<Args>(args)...);
0436         }
0437     }
0438 
0439     /** Invoke the final completion handler.
0440 
0441         This invokes the final completion handler with the specified
0442         arguments forwarded. It is undefined to call either of
0443         @ref boost::beast::async_base::complete or @ref boost::beast::async_base::complete_now more than once.
0444 
0445         Any temporary objects allocated with @ref boost::beast::allocate_stable will
0446         be automatically destroyed before the final completion handler
0447         is invoked.
0448 
0449         @param args A list of optional parameters to invoke the handler
0450         with. The completion handler must be invocable with the parameter
0451         list, or else a compilation error will result.
0452     */
0453     template<class... Args>
0454     void
0455     complete_now(Args&&... args)
0456     {
0457         this->before_invoke_hook();
0458         wg1_.reset();
0459         h_(std::forward<Args>(args)...);
0460     }
0461 
0462 #if ! BOOST_BEAST_DOXYGEN
0463     Handler*
0464     get_legacy_handler_pointer() noexcept
0465     {
0466         return std::addressof(h_);
0467     }
0468 #endif
0469 };
0470 
0471 //------------------------------------------------------------------------------
0472 
0473 /** Base class to provide completion handler boilerplate for composed operations.
0474 
0475     A function object submitted to intermediate initiating functions during
0476     a composed operation may derive from this type to inherit all of the
0477     boilerplate to forward the executor, allocator, and legacy customization
0478     points associated with the completion handler invoked at the end of the
0479     composed operation.
0480 
0481     The composed operation must be typical; that is, associated with one
0482     executor of an I/O object, and invoking a caller-provided completion
0483     handler when the operation is finished. Classes derived from
0484     @ref async_base will acquire these properties:
0485 
0486     @li Ownership of the final completion handler provided upon construction.
0487 
0488     @li If the final handler has an associated allocator, this allocator will
0489         be propagated to the composed operation subclass. Otherwise, the
0490         associated allocator will be the type specified in the allocator
0491         template parameter, or the default of `std::allocator<void>` if the
0492         parameter is omitted.
0493 
0494     @li If the final handler has an associated executor, then it will be used
0495         as the executor associated with the composed operation. Otherwise,
0496         the specified `Executor1` will be the type of executor associated
0497         with the composed operation.
0498 
0499     @li An instance of `net::executor_work_guard` for the instance of `Executor1`
0500         shall be maintained until either the final handler is invoked, or the
0501         operation base is destroyed, whichever comes first.
0502 
0503 
0504     Data members of composed operations implemented as completion handlers
0505     do not have stable addresses, as the composed operation object is move
0506     constructed upon each call to an initiating function. For most operations
0507     this is not a problem. For complex operations requiring stable temporary
0508     storage, the class @ref stable_async_base is provided which offers
0509     additional functionality:
0510 
0511     @li The free function @ref beast::allocate_stable may be used to allocate
0512     one or more temporary objects associated with the composed operation.
0513 
0514     @li Memory for stable temporary objects is allocated using the allocator
0515     associated with the composed operation.
0516 
0517     @li Stable temporary objects are automatically destroyed, and the memory
0518     freed using the associated allocator, either before the final completion
0519     handler is invoked (a Networking requirement) or when the composed operation
0520     is destroyed, whichever occurs first.
0521 
0522     @par Example
0523 
0524     The following code demonstrates how @ref stable_async_base may be be used to
0525     assist authoring an asynchronous initiating function, by providing all of
0526     the boilerplate to manage the final completion handler in a way that maintains
0527     the allocator and executor associations. Furthermore, the operation shown
0528     allocates temporary memory using @ref beast::allocate_stable for the timer and
0529     message, whose addresses must not change between intermediate operations:
0530 
0531     @code
0532 
0533     // Asynchronously send a message multiple times, once per second
0534     template <class AsyncWriteStream, class T, class WriteHandler>
0535     auto async_write_messages(
0536         AsyncWriteStream& stream,
0537         T const& message,
0538         std::size_t repeat_count,
0539         WriteHandler&& handler) ->
0540             typename net::async_result<
0541                 typename std::decay<WriteHandler>::type,
0542                 void(error_code)>::return_type
0543     {
0544         using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
0545         using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
0546 
0547         struct op : base_type, boost::asio::coroutine
0548         {
0549             // This object must have a stable address
0550             struct temporary_data
0551             {
0552                 // Although std::string is in theory movable, most implementations
0553                 // use a "small buffer optimization" which means that we might
0554                 // be submitting a buffer to the write operation and then
0555                 // moving the string, invalidating the buffer. To prevent
0556                 // undefined behavior we store the string object itself at
0557                 // a stable location.
0558                 std::string const message;
0559 
0560                 net::steady_timer timer;
0561 
0562                 temporary_data(std::string message_, net::io_context& ctx)
0563                     : message(std::move(message_))
0564                     , timer(ctx)
0565                 {
0566                 }
0567             };
0568 
0569             AsyncWriteStream& stream_;
0570             std::size_t repeats_;
0571             temporary_data& data_;
0572 
0573             op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
0574                 : base_type(std::move(handler), stream.get_executor())
0575                 , stream_(stream)
0576                 , repeats_(repeats)
0577                 , data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
0578             {
0579                 (*this)(); // start the operation
0580             }
0581 
0582             // Including this file provides the keywords for macro-based coroutines
0583             #include <boost/asio/yield.hpp>
0584 
0585             void operator()(error_code ec = {}, std::size_t = 0)
0586             {
0587                 reenter(*this)
0588                 {
0589                     // If repeats starts at 0 then we must complete immediately. But
0590                     // we can't call the final handler from inside the initiating
0591                     // function, so we post our intermediate handler first. We use
0592                     // net::async_write with an empty buffer instead of calling
0593                     // net::post to avoid an extra function template instantiation, to
0594                     // keep compile times lower and make the resulting executable smaller.
0595                     yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
0596                     while(! ec && repeats_-- > 0)
0597                     {
0598                         // Send the string. We construct a `const_buffer` here to guarantee
0599                         // that we do not create an additional function template instantation
0600                         // of net::async_write, since we already instantiated it above for
0601                         // net::const_buffer.
0602 
0603                         yield net::async_write(stream_,
0604                             net::const_buffer(net::buffer(data_.message)), std::move(*this));
0605                         if(ec)
0606                             break;
0607 
0608                         // Set the timer and wait
0609                         data_.timer.expires_after(std::chrono::seconds(1));
0610                         yield data_.timer.async_wait(std::move(*this));
0611                     }
0612                 }
0613 
0614                 // The base class destroys the temporary data automatically,
0615                 // before invoking the final completion handler
0616                 this->complete_now(ec);
0617             }
0618 
0619             // Including this file undefines the macros for the coroutines
0620             #include <boost/asio/unyield.hpp>
0621         };
0622 
0623         net::async_completion<WriteHandler, void(error_code)> completion(handler);
0624         std::ostringstream os;
0625         os << message;
0626         op(stream, repeat_count, os.str(), completion.completion_handler);
0627         return completion.result.get();
0628     }
0629 
0630     @endcode
0631 
0632     @tparam Handler The type of the completion handler to store.
0633     This type must meet the requirements of <em>CompletionHandler</em>.
0634 
0635     @tparam Executor1 The type of the executor used when the handler has no
0636     associated executor. An instance of this type must be provided upon
0637     construction. The implementation will maintain an executor work guard
0638     and a copy of this instance.
0639 
0640     @tparam Allocator The allocator type to use if the handler does not
0641     have an associated allocator. If this parameter is omitted, then
0642     `std::allocator<void>` will be used. If the specified allocator is
0643     not default constructible, an instance of the type must be provided
0644     upon construction.
0645 
0646     @see allocate_stable, async_base
0647 */
0648 template<
0649     class Handler,
0650     class Executor1,
0651     class Allocator = std::allocator<void>
0652 >
0653 class stable_async_base
0654     : public async_base<
0655         Handler, Executor1, Allocator>
0656 {
0657     detail::stable_base* list_ = nullptr;
0658 
0659     void
0660     before_invoke_hook() override
0661     {
0662         detail::stable_base::destroy_list(list_);
0663     }
0664 
0665 public:
0666     /** Constructor
0667 
0668         @param handler The final completion handler.
0669         The type of this object must meet the requirements of <em>CompletionHandler</em>.
0670         The implementation takes ownership of the handler by performing a decay-copy.
0671 
0672         @param ex1 The executor associated with the implied I/O object
0673         target of the operation. The implementation shall maintain an
0674         executor work guard for the lifetime of the operation, or until
0675         the final completion handler is invoked, whichever is shorter.
0676 
0677         @param alloc The allocator to be associated with objects
0678         derived from this class. If `Allocator` is default-constructible,
0679         this parameter is optional and may be omitted.
0680     */
0681 #if BOOST_BEAST_DOXYGEN
0682     template<class Handler>
0683     stable_async_base(
0684         Handler&& handler,
0685         Executor1 const& ex1,
0686         Allocator const& alloc = Allocator());
0687 #else
0688     template<
0689         class Handler_,
0690         class = typename std::enable_if<
0691             ! std::is_same<typename
0692                 std::decay<Handler_>::type,
0693                 stable_async_base
0694             >::value>::type
0695     >
0696     stable_async_base(
0697         Handler_&& handler,
0698         Executor1 const& ex1)
0699         : async_base<
0700             Handler, Executor1, Allocator>(
0701                 std::forward<Handler_>(handler), ex1)
0702     {
0703     }
0704 
0705     template<class Handler_>
0706     stable_async_base(
0707         Handler_&& handler,
0708         Executor1 const& ex1,
0709         Allocator const& alloc)
0710         : async_base<
0711             Handler, Executor1, Allocator>(
0712                 std::forward<Handler_>(handler), ex1, alloc)
0713     {
0714     }
0715 #endif
0716 
0717     /// Move Constructor
0718     stable_async_base(stable_async_base&& other)
0719         : async_base<Handler, Executor1, Allocator>(
0720             std::move(other))
0721         , list_(boost::exchange(other.list_, nullptr))
0722     {
0723     }
0724 
0725     /** Destructor
0726 
0727         If the completion handler was not invoked, then any
0728         state objects allocated with @ref allocate_stable will
0729         be destroyed here.
0730     */
0731     ~stable_async_base()
0732     {
0733         detail::stable_base::destroy_list(list_);
0734     }
0735 
0736     /** Allocate a temporary object to hold operation state.
0737 
0738         The object will be destroyed just before the completion
0739         handler is invoked, or when the operation base is destroyed.
0740     */
0741     template<
0742         class State,
0743         class Handler_,
0744         class Executor1_,
0745         class Allocator_,
0746         class... Args>
0747     friend
0748     State&
0749     allocate_stable(
0750         stable_async_base<
0751             Handler_, Executor1_, Allocator_>& base,
0752         Args&&... args);
0753 };
0754 
0755 /** Allocate a temporary object to hold stable asynchronous operation state.
0756 
0757     The object will be destroyed just before the completion
0758     handler is invoked, or when the base is destroyed.
0759 
0760     @tparam State The type of object to allocate.
0761 
0762     @param base The helper to allocate from.
0763 
0764     @param args An optional list of parameters to forward to the
0765     constructor of the object being allocated.
0766 
0767     @see stable_async_base
0768 */
0769 template<
0770     class State,
0771     class Handler,
0772     class Executor1,
0773     class Allocator,
0774     class... Args>
0775 State&
0776 allocate_stable(
0777     stable_async_base<
0778         Handler, Executor1, Allocator>& base,
0779     Args&&... args);
0780 
0781 } // beast
0782 } // boost
0783 
0784 #include <boost/beast/core/impl/async_base.hpp>
0785 
0786 #endif