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

source code of boost/libs/beast/include/boost/beast/core/async_base.hpp