1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9 
10 #ifndef BOOST_BEAST_CORE_ASYNC_BASE_HPP
11 #define BOOST_BEAST_CORE_ASYNC_BASE_HPP
12 
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/bind_handler.hpp>
15 #include <boost/beast/core/detail/allocator.hpp>
16 #include <boost/beast/core/detail/async_base.hpp>
17 #include <boost/beast/core/detail/work_guard.hpp>
18 #include <boost/asio/associated_allocator.hpp>
19 #include <boost/asio/associated_executor.hpp>
20 #include <boost/asio/bind_executor.hpp>
21 #include <boost/asio/handler_alloc_hook.hpp>
22 #include <boost/asio/handler_continuation_hook.hpp>
23 #include <boost/asio/handler_invoke_hook.hpp>
24 #include <boost/asio/post.hpp>
25 #include <boost/core/exchange.hpp>
26 #include <boost/core/empty_value.hpp>
27 #include <utility>
28 
29 namespace boost {
30 namespace beast {
31 
32 /** Base class to assist writing composed operations.
33 
34     A function object submitted to intermediate initiating functions during
35     a composed operation may derive from this type to inherit all of the
36     boilerplate to forward the executor, allocator, and legacy customization
37     points associated with the completion handler invoked at the end of the
38     composed operation.
39 
40     The composed operation must be typical; that is, associated with one
41     executor of an I/O object, and invoking a caller-provided completion
42     handler when the operation is finished. Classes derived from
43     @ref async_base will acquire these properties:
44 
45     @li Ownership of the final completion handler provided upon construction.
46 
47     @li If the final handler has an associated allocator, this allocator will
48         be propagated to the composed operation subclass. Otherwise, the
49         associated allocator will be the type specified in the allocator
50         template parameter, or the default of `std::allocator<void>` if the
51         parameter is omitted.
52 
53     @li If the final handler has an associated executor, then it will be used
54         as the executor associated with the composed operation. Otherwise,
55         the specified `Executor1` will be the type of executor associated
56         with the composed operation.
57 
58     @li An instance of `net::executor_work_guard` for the instance of `Executor1`
59         shall be maintained until either the final handler is invoked, or the
60         operation base is destroyed, whichever comes first.
61 
62     @li Calls to the legacy customization points
63         `asio_handler_invoke`,
64         `asio_handler_allocate`,
65         `asio_handler_deallocate`, and
66         `asio_handler_is_continuation`,
67         which use argument-dependent lookup, will be forwarded to the
68         legacy customization points associated with the handler.
69 
70     @par Example
71 
72     The following code demonstrates how @ref async_base may be be used to
73     assist authoring an asynchronous initiating function, by providing all of
74     the boilerplate to manage the final completion handler in a way that
75     maintains the allocator and executor associations:
76 
77     @code
78 
79     // Asynchronously read into a buffer until the buffer is full, or an error occurs
80     template<class AsyncReadStream, class ReadHandler>
81     typename net::async_result<ReadHandler, void(error_code, std::size_t)>::return_type
82     async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
83     {
84         using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
85         using base_type = async_base<handler_type, typename AsyncReadStream::executor_type>;
86 
87         struct op : base_type
88         {
89             AsyncReadStream& stream_;
90             net::mutable_buffer buffer_;
91             std::size_t total_bytes_transferred_;
92 
93             op(
94                 AsyncReadStream& stream,
95                 net::mutable_buffer buffer,
96                 handler_type& handler)
97                 : base_type(std::move(handler), stream.get_executor())
98                 , stream_(stream)
99                 , buffer_(buffer)
100                 , total_bytes_transferred_(0)
101             {
102                 (*this)({}, 0, false); // start the operation
103             }
104 
105             void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
106             {
107                 // Adjust the count of bytes and advance our buffer
108                 total_bytes_transferred_ += bytes_transferred;
109                 buffer_ = buffer_ + bytes_transferred;
110 
111                 // Keep reading until buffer is full or an error occurs
112                 if(! ec && buffer_.size() > 0)
113                     return stream_.async_read_some(buffer_, std::move(*this));
114 
115                 // Call the completion handler with the result. If `is_continuation` is
116                 // false, which happens on the first time through this function, then
117                 // `net::post` will be used to call the completion handler, otherwise
118                 // the completion handler will be invoked directly.
119 
120                 this->complete(is_continuation, ec, total_bytes_transferred_);
121             }
122         };
123 
124         net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
125         op(stream, buffer, init.completion_handler);
126         return init.result.get();
127     }
128 
129     @endcode
130 
131     Data members of composed operations implemented as completion handlers
132     do not have stable addresses, as the composed operation object is move
133     constructed upon each call to an initiating function. For most operations
134     this is not a problem. For complex operations requiring stable temporary
135     storage, the class @ref stable_async_base is provided which offers
136     additional functionality:
137 
138     @li The free function @ref allocate_stable may be used to allocate
139     one or more temporary objects associated with the composed operation.
140 
141     @li Memory for stable temporary objects is allocated using the allocator
142     associated with the composed operation.
143 
144     @li Stable temporary objects are automatically destroyed, and the memory
145     freed using the associated allocator, either before the final completion
146     handler is invoked (a Networking requirement) or when the composed operation
147     is destroyed, whichever occurs first.
148 
149     @par Temporary Storage Example
150 
151     The following example demonstrates how a composed operation may store a
152     temporary object.
153 
154     @code
155 
156     @endcode
157 
158     @tparam Handler The type of the completion handler to store.
159     This type must meet the requirements of <em>CompletionHandler</em>.
160 
161     @tparam Executor1 The type of the executor used when the handler has no
162     associated executor. An instance of this type must be provided upon
163     construction. The implementation will maintain an executor work guard
164     and a copy of this instance.
165 
166     @tparam Allocator The allocator type to use if the handler does not
167     have an associated allocator. If this parameter is omitted, then
168     `std::allocator<void>` will be used. If the specified allocator is
169     not default constructible, an instance of the type must be provided
170     upon construction.
171 
172     @see stable_async_base
173 */
174 template<
175     class Handler,
176     class Executor1,
177     class Allocator = std::allocator<void>
178 >
179 class async_base
180 #if ! BOOST_BEAST_DOXYGEN
181     : private boost::empty_value<Allocator>
182 #endif
183 {
184     static_assert(
185         net::is_executor<Executor1>::value || net::execution::is_executor<Executor1>::value,
186         "Executor type requirements not met");
187 
188     Handler h_;
189     detail::select_work_guard_t<Executor1> wg1_;
190 
191 public:
192     /** The type of executor associated with this object.
193 
194     If a class derived from @ref async_base is a completion
195     handler, then the associated executor of the derived class will
196     be this type.
197 */
198     using executor_type =
199 #if BOOST_BEAST_DOXYGEN
200         __implementation_defined__;
201 #else
202         typename
203         net::associated_executor<
204             Handler,
205             typename detail::select_work_guard_t<Executor1>::executor_type
206                 >::type;
207 #endif
208 
209 private:
210 
211     virtual
212     void
before_invoke_hook()213     before_invoke_hook()
214     {
215     }
216 
217 public:
218     /** Constructor
219 
220         @param handler The final completion handler.
221         The type of this object must meet the requirements of <em>CompletionHandler</em>.
222         The implementation takes ownership of the handler by performing a decay-copy.
223 
224         @param ex1 The executor associated with the implied I/O object
225         target of the operation. The implementation shall maintain an
226         executor work guard for the lifetime of the operation, or until
227         the final completion handler is invoked, whichever is shorter.
228 
229         @param alloc The allocator to be associated with objects
230         derived from this class. If `Allocator` is default-constructible,
231         this parameter is optional and may be omitted.
232     */
233 #if BOOST_BEAST_DOXYGEN
234     template<class Handler_>
235     async_base(
236         Handler&& handler,
237         Executor1 const& ex1,
238         Allocator const& alloc = Allocator());
239 #else
240     template<
241         class Handler_,
242         class = typename std::enable_if<
243             ! std::is_same<typename
244                 std::decay<Handler_>::type,
245                 async_base
246             >::value>::type
247     >
248     async_base(
249         Handler_&& handler,
250         Executor1 const& ex1)
251         : h_(std::forward<Handler_>(handler))
252         , wg1_(detail::make_work_guard(ex1))
253     {
254     }
255 
256     template<class Handler_>
257     async_base(
258         Handler_&& handler,
259         Executor1 const& ex1,
260         Allocator const& alloc)
261         : boost::empty_value<Allocator>(
262             boost::empty_init_t{}, alloc)
263         , h_(std::forward<Handler_>(handler))
264         , wg1_(ex1)
265     {
266     }
267 #endif
268 
269     /// Move Constructor
270     async_base(async_base&& other) = default;
271 
272     virtual ~async_base() = default;
273     async_base(async_base const&) = delete;
274     async_base& operator=(async_base const&) = delete;
275 
276     /** The type of allocator associated with this object.
277 
278         If a class derived from @ref async_base is a completion
279         handler, then the associated allocator of the derived class will
280         be this type.
281     */
282     using allocator_type =
283         net::associated_allocator_t<Handler, Allocator>;
284 
285     /** Returns the allocator associated with this object.
286 
287         If a class derived from @ref async_base is a completion
288         handler, then the object returned from this function will be used
289         as the associated allocator of the derived class.
290     */
291     allocator_type
get_allocator() const292     get_allocator() const noexcept
293     {
294         return net::get_associated_allocator(h_,
295             boost::empty_value<Allocator>::get());
296     }
297 
298     /** Returns the executor associated with this object.
299 
300         If a class derived from @ref async_base is a completion
301         handler, then the object returned from this function will be used
302         as the associated executor of the derived class.
303     */
304     executor_type
get_executor() const305     get_executor() const noexcept
306     {
307         return net::get_associated_executor(
308             h_, wg1_.get_executor());
309     }
310 
311     /// Returns the handler associated with this object
312     Handler const&
handler() const313     handler() const noexcept
314     {
315         return h_;
316     }
317 
318     /** Returns ownership of the handler associated with this object
319 
320         This function is used to transfer ownership of the handler to
321         the caller, by move-construction. After the move, the only
322         valid operations on the base object are move construction and
323         destruction.
324     */
325     Handler
release_handler()326     release_handler()
327     {
328         return std::move(h_);
329     }
330 
331     /** Invoke the final completion handler, maybe using post.
332 
333         This invokes the final completion handler with the specified
334         arguments forwarded. It is undefined to call either of
335         @ref complete or @ref complete_now more than once.
336 
337         Any temporary objects allocated with @ref beast::allocate_stable will
338         be automatically destroyed before the final completion handler
339         is invoked.
340 
341         @param is_continuation If this value is `false`, then the
342         handler will be submitted to the executor using `net::post`.
343         Otherwise the handler will be invoked as if by calling
344         @ref complete_now.
345 
346         @param args A list of optional parameters to invoke the handler
347         with. The completion handler must be invocable with the parameter
348         list, or else a compilation error will result.
349     */
350     template<class... Args>
351     void
complete(bool is_continuation,Args &&...args)352     complete(bool is_continuation, Args&&... args)
353     {
354         this->before_invoke_hook();
355         if(! is_continuation)
356         {
357             auto const ex = get_executor();
358             net::post(net::bind_executor(
359                 ex,
360                 beast::bind_front_handler(
361                     std::move(h_),
362                     std::forward<Args>(args)...)));
363             wg1_.reset();
364         }
365         else
366         {
367             wg1_.reset();
368             h_(std::forward<Args>(args)...);
369         }
370     }
371 
372     /** Invoke the final completion handler.
373 
374         This invokes the final completion handler with the specified
375         arguments forwarded. It is undefined to call either of
376         @ref complete or @ref complete_now more than once.
377 
378         Any temporary objects allocated with @ref beast::allocate_stable will
379         be automatically destroyed before the final completion handler
380         is invoked.
381 
382         @param args A list of optional parameters to invoke the handler
383         with. The completion handler must be invocable with the parameter
384         list, or else a compilation error will result.
385     */
386     template<class... Args>
387     void
complete_now(Args &&...args)388     complete_now(Args&&... args)
389     {
390         this->before_invoke_hook();
391         wg1_.reset();
392         h_(std::forward<Args>(args)...);
393     }
394 
395 #if ! BOOST_BEAST_DOXYGEN
396     Handler*
get_legacy_handler_pointer()397     get_legacy_handler_pointer() noexcept
398     {
399         return std::addressof(h_);
400     }
401 #endif
402 };
403 
404 //------------------------------------------------------------------------------
405 
406 /** Base class to provide completion handler boilerplate for composed operations.
407 
408     A function object submitted to intermediate initiating functions during
409     a composed operation may derive from this type to inherit all of the
410     boilerplate to forward the executor, allocator, and legacy customization
411     points associated with the completion handler invoked at the end of the
412     composed operation.
413 
414     The composed operation must be typical; that is, associated with one
415     executor of an I/O object, and invoking a caller-provided completion
416     handler when the operation is finished. Classes derived from
417     @ref async_base will acquire these properties:
418 
419     @li Ownership of the final completion handler provided upon construction.
420 
421     @li If the final handler has an associated allocator, this allocator will
422         be propagated to the composed operation subclass. Otherwise, the
423         associated allocator will be the type specified in the allocator
424         template parameter, or the default of `std::allocator<void>` if the
425         parameter is omitted.
426 
427     @li If the final handler has an associated executor, then it will be used
428         as the executor associated with the composed operation. Otherwise,
429         the specified `Executor1` will be the type of executor associated
430         with the composed operation.
431 
432     @li An instance of `net::executor_work_guard` for the instance of `Executor1`
433         shall be maintained until either the final handler is invoked, or the
434         operation base is destroyed, whichever comes first.
435 
436     @li Calls to the legacy customization points
437         `asio_handler_invoke`,
438         `asio_handler_allocate`,
439         `asio_handler_deallocate`, and
440         `asio_handler_is_continuation`,
441         which use argument-dependent lookup, will be forwarded to the
442         legacy customization points associated with the handler.
443 
444     Data members of composed operations implemented as completion handlers
445     do not have stable addresses, as the composed operation object is move
446     constructed upon each call to an initiating function. For most operations
447     this is not a problem. For complex operations requiring stable temporary
448     storage, the class @ref stable_async_base is provided which offers
449     additional functionality:
450 
451     @li The free function @ref beast::allocate_stable may be used to allocate
452     one or more temporary objects associated with the composed operation.
453 
454     @li Memory for stable temporary objects is allocated using the allocator
455     associated with the composed operation.
456 
457     @li Stable temporary objects are automatically destroyed, and the memory
458     freed using the associated allocator, either before the final completion
459     handler is invoked (a Networking requirement) or when the composed operation
460     is destroyed, whichever occurs first.
461 
462     @par Example
463 
464     The following code demonstrates how @ref stable_async_base may be be used to
465     assist authoring an asynchronous initiating function, by providing all of
466     the boilerplate to manage the final completion handler in a way that maintains
467     the allocator and executor associations. Furthermore, the operation shown
468     allocates temporary memory using @ref beast::allocate_stable for the timer and
469     message, whose addresses must not change between intermediate operations:
470 
471     @code
472 
473     // Asynchronously send a message multiple times, once per second
474     template <class AsyncWriteStream, class T, class WriteHandler>
475     auto async_write_messages(
476         AsyncWriteStream& stream,
477         T const& message,
478         std::size_t repeat_count,
479         WriteHandler&& handler) ->
480             typename net::async_result<
481                 typename std::decay<WriteHandler>::type,
482                 void(error_code)>::return_type
483     {
484         using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
485         using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
486 
487         struct op : base_type, boost::asio::coroutine
488         {
489             // This object must have a stable address
490             struct temporary_data
491             {
492                 // Although std::string is in theory movable, most implementations
493                 // use a "small buffer optimization" which means that we might
494                 // be submitting a buffer to the write operation and then
495                 // moving the string, invalidating the buffer. To prevent
496                 // undefined behavior we store the string object itself at
497                 // a stable location.
498                 std::string const message;
499 
500                 net::steady_timer timer;
501 
502                 temporary_data(std::string message_, net::io_context& ctx)
503                     : message(std::move(message_))
504                     , timer(ctx)
505                 {
506                 }
507             };
508 
509             AsyncWriteStream& stream_;
510             std::size_t repeats_;
511             temporary_data& data_;
512 
513             op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
514                 : base_type(std::move(handler), stream.get_executor())
515                 , stream_(stream)
516                 , repeats_(repeats)
517                 , data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
518             {
519                 (*this)(); // start the operation
520             }
521 
522             // Including this file provides the keywords for macro-based coroutines
523             #include <boost/asio/yield.hpp>
524 
525             void operator()(error_code ec = {}, std::size_t = 0)
526             {
527                 reenter(*this)
528                 {
529                     // If repeats starts at 0 then we must complete immediately. But
530                     // we can't call the final handler from inside the initiating
531                     // function, so we post our intermediate handler first. We use
532                     // net::async_write with an empty buffer instead of calling
533                     // net::post to avoid an extra function template instantiation, to
534                     // keep compile times lower and make the resulting executable smaller.
535                     yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
536                     while(! ec && repeats_-- > 0)
537                     {
538                         // Send the string. We construct a `const_buffer` here to guarantee
539                         // that we do not create an additional function template instantation
540                         // of net::async_write, since we already instantiated it above for
541                         // net::const_buffer.
542 
543                         yield net::async_write(stream_,
544                             net::const_buffer(net::buffer(data_.message)), std::move(*this));
545                         if(ec)
546                             break;
547 
548                         // Set the timer and wait
549                         data_.timer.expires_after(std::chrono::seconds(1));
550                         yield data_.timer.async_wait(std::move(*this));
551                     }
552                 }
553 
554                 // The base class destroys the temporary data automatically,
555                 // before invoking the final completion handler
556                 this->complete_now(ec);
557             }
558 
559             // Including this file undefines the macros for the coroutines
560             #include <boost/asio/unyield.hpp>
561         };
562 
563         net::async_completion<WriteHandler, void(error_code)> completion(handler);
564         std::ostringstream os;
565         os << message;
566         op(stream, repeat_count, os.str(), completion.completion_handler);
567         return completion.result.get();
568     }
569 
570     @endcode
571 
572     @tparam Handler The type of the completion handler to store.
573     This type must meet the requirements of <em>CompletionHandler</em>.
574 
575     @tparam Executor1 The type of the executor used when the handler has no
576     associated executor. An instance of this type must be provided upon
577     construction. The implementation will maintain an executor work guard
578     and a copy of this instance.
579 
580     @tparam Allocator The allocator type to use if the handler does not
581     have an associated allocator. If this parameter is omitted, then
582     `std::allocator<void>` will be used. If the specified allocator is
583     not default constructible, an instance of the type must be provided
584     upon construction.
585 
586     @see allocate_stable, async_base
587 */
588 template<
589     class Handler,
590     class Executor1,
591     class Allocator = std::allocator<void>
592 >
593 class stable_async_base
594     : public async_base<
595         Handler, Executor1, Allocator>
596 {
597     detail::stable_base* list_ = nullptr;
598 
599     void
before_invoke_hook()600     before_invoke_hook() override
601     {
602         detail::stable_base::destroy_list(list_);
603     }
604 
605 public:
606     /** Constructor
607 
608         @param handler The final completion handler.
609         The type of this object must meet the requirements of <em>CompletionHandler</em>.
610         The implementation takes ownership of the handler by performing a decay-copy.
611 
612         @param ex1 The executor associated with the implied I/O object
613         target of the operation. The implementation shall maintain an
614         executor work guard for the lifetime of the operation, or until
615         the final completion handler is invoked, whichever is shorter.
616 
617         @param alloc The allocator to be associated with objects
618         derived from this class. If `Allocator` is default-constructible,
619         this parameter is optional and may be omitted.
620     */
621 #if BOOST_BEAST_DOXYGEN
622     template<class Handler>
623     stable_async_base(
624         Handler&& handler,
625         Executor1 const& ex1,
626         Allocator const& alloc = Allocator());
627 #else
628     template<
629         class Handler_,
630         class = typename std::enable_if<
631             ! std::is_same<typename
632                 std::decay<Handler_>::type,
633                 stable_async_base
634             >::value>::type
635     >
636     stable_async_base(
637         Handler_&& handler,
638         Executor1 const& ex1)
639         : async_base<
640             Handler, Executor1, Allocator>(
641                 std::forward<Handler_>(handler), ex1)
642     {
643     }
644 
645     template<class Handler_>
646     stable_async_base(
647         Handler_&& handler,
648         Executor1 const& ex1,
649         Allocator const& alloc)
650         : async_base<
651             Handler, Executor1, Allocator>(
652                 std::forward<Handler_>(handler), ex1, alloc)
653     {
654     }
655 #endif
656 
657     /// Move Constructor
stable_async_base(stable_async_base && other)658     stable_async_base(stable_async_base&& other)
659         : async_base<Handler, Executor1, Allocator>(
660             std::move(other))
661         , list_(boost::exchange(other.list_, nullptr))
662     {
663     }
664 
665     /** Destructor
666 
667         If the completion handler was not invoked, then any
668         state objects allocated with @ref allocate_stable will
669         be destroyed here.
670     */
~stable_async_base()671     ~stable_async_base()
672     {
673         detail::stable_base::destroy_list(list_);
674     }
675 
676     /** Allocate a temporary object to hold operation state.
677 
678         The object will be destroyed just before the completion
679         handler is invoked, or when the operation base is destroyed.
680     */
681     template<
682         class State,
683         class Handler_,
684         class Executor1_,
685         class Allocator_,
686         class... Args>
687     friend
688     State&
689     allocate_stable(
690         stable_async_base<
691             Handler_, Executor1_, Allocator_>& base,
692         Args&&... args);
693 };
694 
695 /** Allocate a temporary object to hold stable asynchronous operation state.
696 
697     The object will be destroyed just before the completion
698     handler is invoked, or when the base is destroyed.
699 
700     @tparam State The type of object to allocate.
701 
702     @param base The helper to allocate from.
703 
704     @param args An optional list of parameters to forward to the
705     constructor of the object being allocated.
706 
707     @see stable_async_base
708 */
709 template<
710     class State,
711     class Handler,
712     class Executor1,
713     class Allocator,
714     class... Args>
715 State&
716 allocate_stable(
717     stable_async_base<
718         Handler, Executor1, Allocator>& base,
719     Args&&... args);
720 
721 } // beast
722 } // boost
723 
724 #include <boost/beast/core/impl/async_base.hpp>
725 
726 #endif
727