]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/core/async_base.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / 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/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
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
292 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
305 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&
313 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
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
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
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*
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
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
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 */
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