Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / beast / core / async_base.hpp
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/asio/associated_allocator.hpp>
18 #include <boost/asio/associated_executor.hpp>
19 #include <boost/asio/bind_executor.hpp>
20 #include <boost/asio/executor_work_guard.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,
186         "Executor type requirements not met");
187
188     Handler h_;
189     net::executor_work_guard<Executor1> wg1_;
190
191     virtual
192     void
193     before_invoke_hook()
194     {
195     }
196
197 public:
198     /** Constructor
199
200         @param handler The final completion handler.
201         The type of this object must meet the requirements of <em>CompletionHandler</em>.
202         The implementation takes ownership of the handler by performing a decay-copy.
203
204         @param ex1 The executor associated with the implied I/O object
205         target of the operation. The implementation shall maintain an
206         executor work guard for the lifetime of the operation, or until
207         the final completion handler is invoked, whichever is shorter.
208
209         @param alloc The allocator to be associated with objects
210         derived from this class. If `Allocator` is default-constructible,
211         this parameter is optional and may be omitted.
212     */
213 #if BOOST_BEAST_DOXYGEN
214     template<class Handler_>
215     async_base(
216         Handler&& handler,
217         Executor1 const& ex1,
218         Allocator const& alloc = Allocator());
219 #else
220     template<
221         class Handler_,
222         class = typename std::enable_if<
223             ! std::is_same<typename
224                 std::decay<Handler_>::type,
225                 async_base
226             >::value>::type
227     >
228     async_base(
229         Handler_&& handler,
230         Executor1 const& ex1)
231         : h_(std::forward<Handler_>(handler))
232         , wg1_(ex1)
233     {
234     }
235
236     template<class Handler_>
237     async_base(
238         Handler_&& handler,
239         Executor1 const& ex1,
240         Allocator const& alloc)
241         : boost::empty_value<Allocator>(
242             boost::empty_init_t{}, alloc)
243         , h_(std::forward<Handler_>(handler))
244         , wg1_(ex1)
245     {
246     }
247 #endif
248
249     /// Move Constructor
250     async_base(async_base&& other) = default;
251
252     virtual ~async_base() = default;
253     async_base(async_base const&) = delete;
254     async_base& operator=(async_base const&) = delete;
255
256     /** The type of allocator associated with this object.
257
258         If a class derived from @ref async_base is a completion
259         handler, then the associated allocator of the derived class will
260         be this type.
261     */
262     using allocator_type =
263         net::associated_allocator_t<Handler, Allocator>;
264
265     /** The type of executor associated with this object.
266
267         If a class derived from @ref async_base is a completion
268         handler, then the associated executor of the derived class will
269         be this type.
270     */
271     using executor_type =
272         net::associated_executor_t<Handler, Executor1>;
273
274     /** Returns the allocator associated with this object.
275
276         If a class derived from @ref async_base is a completion
277         handler, then the object returned from this function will be used
278         as the associated allocator of the derived class.
279     */
280     allocator_type
281     get_allocator() const noexcept
282     {
283         return net::get_associated_allocator(h_,
284             boost::empty_value<Allocator>::get());
285     }
286
287     /** Returns the executor associated with this object.
288
289         If a class derived from @ref async_base is a completion
290         handler, then the object returned from this function will be used
291         as the associated executor of the derived class.
292     */
293     executor_type
294     get_executor() const noexcept
295     {
296         return net::get_associated_executor(
297             h_, wg1_.get_executor());
298     }
299
300     /// Returns the handler associated with this object
301     Handler const&
302     handler() const noexcept
303     {
304         return h_;
305     }
306
307     /** Returns ownership of the handler associated with this object
308
309         This function is used to transfer ownership of the handler to
310         the caller, by move-construction. After the move, the only
311         valid operations on the base object are move construction and
312         destruction.
313     */
314     Handler
315     release_handler()
316     {
317         return std::move(h_);
318     }
319
320     /** Invoke the final completion handler, maybe using post.
321
322         This invokes the final completion handler with the specified
323         arguments forwarded. It is undefined to call either of
324         @ref complete or @ref complete_now more than once.
325
326         Any temporary objects allocated with @ref beast::allocate_stable will
327         be automatically destroyed before the final completion handler
328         is invoked.
329
330         @param is_continuation If this value is `false`, then the
331         handler will be submitted to the executor using `net::post`.
332         Otherwise the handler will be invoked as if by calling
333         @ref complete_now.
334
335         @param args A list of optional parameters to invoke the handler
336         with. The completion handler must be invocable with the parameter
337         list, or else a compilation error will result.
338     */
339     template<class... Args>
340     void
341     complete(bool is_continuation, Args&&... args)
342     {
343         this->before_invoke_hook();
344         if(! is_continuation)
345         {
346             auto const ex = get_executor();
347             net::post(net::bind_executor(
348                 ex,
349                 beast::bind_front_handler(
350                     std::move(h_),
351                     std::forward<Args>(args)...)));
352             wg1_.reset();
353         }
354         else
355         {
356             wg1_.reset();
357             h_(std::forward<Args>(args)...);
358         }
359     }
360
361     /** Invoke the final completion handler.
362
363         This invokes the final completion handler with the specified
364         arguments forwarded. It is undefined to call either of
365         @ref complete or @ref complete_now more than once.
366
367         Any temporary objects allocated with @ref beast::allocate_stable will
368         be automatically destroyed before the final completion handler
369         is invoked.
370
371         @param args A list of optional parameters to invoke the handler
372         with. The completion handler must be invocable with the parameter
373         list, or else a compilation error will result.
374     */
375     template<class... Args>
376     void
377     complete_now(Args&&... args)
378     {
379         this->before_invoke_hook();
380         wg1_.reset();
381         h_(std::forward<Args>(args)...);
382     }
383
384 #if ! BOOST_BEAST_DOXYGEN
385     Handler*
386     get_legacy_handler_pointer() noexcept
387     {
388         return std::addressof(h_);
389     }
390 #endif
391 };
392
393 //------------------------------------------------------------------------------
394
395 /** Base class to provide completion handler boilerplate for composed operations.
396
397     A function object submitted to intermediate initiating functions during
398     a composed operation may derive from this type to inherit all of the
399     boilerplate to forward the executor, allocator, and legacy customization
400     points associated with the completion handler invoked at the end of the
401     composed operation.
402
403     The composed operation must be typical; that is, associated with one
404     executor of an I/O object, and invoking a caller-provided completion
405     handler when the operation is finished. Classes derived from
406     @ref async_base will acquire these properties:
407
408     @li Ownership of the final completion handler provided upon construction.
409
410     @li If the final handler has an associated allocator, this allocator will
411         be propagated to the composed operation subclass. Otherwise, the
412         associated allocator will be the type specified in the allocator
413         template parameter, or the default of `std::allocator<void>` if the
414         parameter is omitted.
415
416     @li If the final handler has an associated executor, then it will be used
417         as the executor associated with the composed operation. Otherwise,
418         the specified `Executor1` will be the type of executor associated
419         with the composed operation.
420
421     @li An instance of `net::executor_work_guard` for the instance of `Executor1`
422         shall be maintained until either the final handler is invoked, or the
423         operation base is destroyed, whichever comes first.
424
425     @li Calls to the legacy customization points
426         `asio_handler_invoke`,
427         `asio_handler_allocate`,
428         `asio_handler_deallocate`, and
429         `asio_handler_is_continuation`,
430         which use argument-dependent lookup, will be forwarded to the
431         legacy customization points associated with the handler.
432
433     Data members of composed operations implemented as completion handlers
434     do not have stable addresses, as the composed operation object is move
435     constructed upon each call to an initiating function. For most operations
436     this is not a problem. For complex operations requiring stable temporary
437     storage, the class @ref stable_async_base is provided which offers
438     additional functionality:
439
440     @li The free function @ref beast::allocate_stable may be used to allocate
441     one or more temporary objects associated with the composed operation.
442
443     @li Memory for stable temporary objects is allocated using the allocator
444     associated with the composed operation.
445
446     @li Stable temporary objects are automatically destroyed, and the memory
447     freed using the associated allocator, either before the final completion
448     handler is invoked (a Networking requirement) or when the composed operation
449     is destroyed, whichever occurs first.
450
451     @par Example
452
453     The following code demonstrates how @ref stable_async_base may be be used to
454     assist authoring an asynchronous initiating function, by providing all of
455     the boilerplate to manage the final completion handler in a way that maintains
456     the allocator and executor associations. Furthermore, the operation shown
457     allocates temporary memory using @ref beast::allocate_stable for the timer and
458     message, whose addresses must not change between intermediate operations:
459
460     @code
461
462     // Asynchronously send a message multiple times, once per second
463     template <class AsyncWriteStream, class T, class WriteHandler>
464     auto async_write_messages(
465         AsyncWriteStream& stream,
466         T const& message,
467         std::size_t repeat_count,
468         WriteHandler&& handler) ->
469             typename net::async_result<
470                 typename std::decay<WriteHandler>::type,
471                 void(error_code)>::return_type
472     {
473         using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
474         using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
475
476         struct op : base_type, boost::asio::coroutine
477         {
478             // This object must have a stable address
479             struct temporary_data
480             {
481                 // Although std::string is in theory movable, most implementations
482                 // use a "small buffer optimization" which means that we might
483                 // be submitting a buffer to the write operation and then
484                 // moving the string, invalidating the buffer. To prevent
485                 // undefined behavior we store the string object itself at
486                 // a stable location.
487                 std::string const message;
488
489                 net::steady_timer timer;
490
491                 temporary_data(std::string message_, net::io_context& ctx)
492                     : message(std::move(message_))
493                     , timer(ctx)
494                 {
495                 }
496             };
497
498             AsyncWriteStream& stream_;
499             std::size_t repeats_;
500             temporary_data& data_;
501
502             op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
503                 : base_type(std::move(handler), stream.get_executor())
504                 , stream_(stream)
505                 , repeats_(repeats)
506                 , data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
507             {
508                 (*this)(); // start the operation
509             }
510
511             // Including this file provides the keywords for macro-based coroutines
512             #include <boost/asio/yield.hpp>
513
514             void operator()(error_code ec = {}, std::size_t = 0)
515             {
516                 reenter(*this)
517                 {
518                     // If repeats starts at 0 then we must complete immediately. But
519                     // we can't call the final handler from inside the initiating
520                     // function, so we post our intermediate handler first. We use
521                     // net::async_write with an empty buffer instead of calling
522                     // net::post to avoid an extra function template instantiation, to
523                     // keep compile times lower and make the resulting executable smaller.
524                     yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
525                     while(! ec && repeats_-- > 0)
526                     {
527                         // Send the string. We construct a `const_buffer` here to guarantee
528                         // that we do not create an additional function template instantation
529                         // of net::async_write, since we already instantiated it above for
530                         // net::const_buffer.
531
532                         yield net::async_write(stream_,
533                             net::const_buffer(net::buffer(data_.message)), std::move(*this));
534                         if(ec)
535                             break;
536
537                         // Set the timer and wait
538                         data_.timer.expires_after(std::chrono::seconds(1));
539                         yield data_.timer.async_wait(std::move(*this));
540                     }
541                 }
542
543                 // The base class destroys the temporary data automatically,
544                 // before invoking the final completion handler
545                 this->complete_now(ec);
546             }
547
548             // Including this file undefines the macros for the coroutines
549             #include <boost/asio/unyield.hpp>
550         };
551
552         net::async_completion<WriteHandler, void(error_code)> completion(handler);
553         std::ostringstream os;
554         os << message;
555         op(stream, repeat_count, os.str(), completion.completion_handler);
556         return completion.result.get();
557     }
558
559     @endcode
560
561     @tparam Handler The type of the completion handler to store.
562     This type must meet the requirements of <em>CompletionHandler</em>.
563
564     @tparam Executor1 The type of the executor used when the handler has no
565     associated executor. An instance of this type must be provided upon
566     construction. The implementation will maintain an executor work guard
567     and a copy of this instance.
568
569     @tparam Allocator The allocator type to use if the handler does not
570     have an associated allocator. If this parameter is omitted, then
571     `std::allocator<void>` will be used. If the specified allocator is
572     not default constructible, an instance of the type must be provided
573     upon construction.
574
575     @see allocate_stable, async_base
576 */
577 template<
578     class Handler,
579     class Executor1,
580     class Allocator = std::allocator<void>
581 >
582 class stable_async_base
583     : public async_base<
584         Handler, Executor1, Allocator>
585 {
586     detail::stable_base* list_ = nullptr;
587
588     void
589     before_invoke_hook() override
590     {
591         detail::stable_base::destroy_list(list_);
592     }
593
594 public:
595     /** Constructor
596
597         @param handler The final completion handler.
598         The type of this object must meet the requirements of <em>CompletionHandler</em>.
599         The implementation takes ownership of the handler by performing a decay-copy.
600
601         @param ex1 The executor associated with the implied I/O object
602         target of the operation. The implementation shall maintain an
603         executor work guard for the lifetime of the operation, or until
604         the final completion handler is invoked, whichever is shorter.
605
606         @param alloc The allocator to be associated with objects
607         derived from this class. If `Allocator` is default-constructible,
608         this parameter is optional and may be omitted.
609     */
610 #if BOOST_BEAST_DOXYGEN
611     template<class Handler>
612     stable_async_base(
613         Handler&& handler,
614         Executor1 const& ex1,
615         Allocator const& alloc = Allocator());
616 #else
617     template<
618         class Handler_,
619         class = typename std::enable_if<
620             ! std::is_same<typename
621                 std::decay<Handler_>::type,
622                 stable_async_base
623             >::value>::type
624     >
625     stable_async_base(
626         Handler_&& handler,
627         Executor1 const& ex1)
628         : async_base<
629             Handler, Executor1, Allocator>(
630                 std::forward<Handler_>(handler), ex1)
631     {
632     }
633
634     template<class Handler_>
635     stable_async_base(
636         Handler_&& handler,
637         Executor1 const& ex1,
638         Allocator const& alloc)
639         : async_base<
640             Handler, Executor1, Allocator>(
641                 std::forward<Handler_>(handler), ex1, alloc)
642     {
643     }
644 #endif
645
646     /// Move Constructor
647     stable_async_base(stable_async_base&& other)
648         : async_base<Handler, Executor1, Allocator>(
649             std::move(other))
650         , list_(boost::exchange(other.list_, nullptr))
651     {
652     }
653
654     /** Destructor
655
656         If the completion handler was not invoked, then any
657         state objects allocated with @ref allocate_stable will
658         be destroyed here.
659     */
660     ~stable_async_base()
661     {
662         detail::stable_base::destroy_list(list_);
663     }
664
665     /** Allocate a temporary object to hold operation state.
666
667         The object will be destroyed just before the completion
668         handler is invoked, or when the operation base is destroyed.
669     */
670     template<
671         class State,
672         class Handler_,
673         class Executor1_,
674         class Allocator_,
675         class... Args>
676     friend
677     State&
678     allocate_stable(
679         stable_async_base<
680             Handler_, Executor1_, Allocator_>& base,
681         Args&&... args);
682 };
683
684 /** Allocate a temporary object to hold stable asynchronous operation state.
685
686     The object will be destroyed just before the completion
687     handler is invoked, or when the base is destroyed.
688
689     @tparam State The type of object to allocate.
690
691     @param base The helper to allocate from.
692
693     @param args An optional list of parameters to forward to the
694     constructor of the object being allocated.
695
696     @see stable_async_base
697 */
698 template<
699     class State,
700     class Handler,
701     class Executor1,
702     class Allocator,
703     class... Args>
704 State&
705 allocate_stable(
706     stable_async_base<
707         Handler, Executor1, Allocator>& base,
708     Args&&... args);
709
710 } // beast
711 } // boost
712
713 #include <boost/beast/core/impl/async_base.hpp>
714
715 #endif