Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-02 08:07:13

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