]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/beast/test/beast/core/async_base.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / beast / test / beast / core / async_base.cpp
CommitLineData
92f5a8d4
TL
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// Test that header file is self-contained.
11#include <boost/beast/core/async_base.hpp>
12
13#include "test_handler.hpp"
14
15#include <boost/beast/_experimental/unit_test/suite.hpp>
16#include <boost/beast/_experimental/test/handler.hpp>
17#include <boost/beast/_experimental/test/stream.hpp>
18#include <boost/beast/core/error.hpp>
19#include <boost/asio/async_result.hpp>
20#include <boost/asio/coroutine.hpp>
21#include <boost/asio/io_context.hpp>
22#include <boost/asio/steady_timer.hpp>
23#include <boost/asio/system_executor.hpp>
24#include <boost/asio/write.hpp>
25#include <boost/core/ignore_unused.hpp>
26#include <stdexcept>
27
20effc67 28
92f5a8d4
TL
29//------------------------------------------------------------------------------
30
31namespace boost {
32namespace beast {
33
34namespace {
35
20effc67
TL
36#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
37struct ex1_type
38{
39
40 net::execution_context &
41 query(net::execution::context_t c) const noexcept
42 { return *reinterpret_cast<net::execution_context *>(0); }
43
44 net::execution::blocking_t
45 query(net::execution::blocking_t) const noexcept
46 { return net::execution::blocking; };
47
48 net::execution::outstanding_work_t
49 query(net::execution::outstanding_work_t w) const noexcept
50 { return net::execution::outstanding_work; }
51
52 ex1_type
53 require(net::execution::blocking_t::possibly_t b) const
54 { return *this; }
55
56 ex1_type
57 require(net::execution::blocking_t::never_t b) const
58 { return *this; };
59
60 ex1_type
61 prefer(net::execution::outstanding_work_t::untracked_t w) const
62 { return *this; };
63
64 ex1_type
65 prefer(net::execution::outstanding_work_t::tracked_t w) const
66 { return *this; };
67
68 template<class F>
69 void
70 execute(F &&) const
71 {}
72
73 bool
74 operator==(ex1_type const &) const noexcept
75 { return true; }
76 bool
77 operator!=(ex1_type const &) const noexcept
78 { return false; }
79};
80BOOST_STATIC_ASSERT(net::execution::is_executor<ex1_type>::value);
81#else
92f5a8d4
TL
82struct ex1_type
83{
84 void* context() { return nullptr; }
85 void on_work_started() {}
86 void on_work_finished() {}
87 template<class F> void dispatch(F&&) {}
88 template<class F> void post(F&&) {}
89 template<class F> void defer(F&&) {}
90};
20effc67
TL
91BOOST_STATIC_ASSERT(net::is_executor<ex1_type>::value);
92#endif
93
92f5a8d4
TL
94
95struct no_alloc
96{
97};
98
99struct nested_alloc
100{
101 struct allocator_type
102 {
103 };
104};
105
106struct intrusive_alloc
107{
108 struct allocator_type
109 {
110 };
111};
112
113struct no_ex
114{
115 using executor_type = net::system_executor;
116};
117
118struct nested_ex
119{
120 struct executor_type
121 {
122 };
123};
124
125struct intrusive_ex
126{
127 struct executor_type
128 {
129 };
130};
131
132template<class E, class A>
133struct handler;
134
135template<>
136struct handler<no_ex, no_alloc>
137{
138};
139
140template<>
141struct handler<no_ex, nested_alloc>
142 : nested_alloc
143{
144};
145
146template<>
147struct handler<no_ex, intrusive_alloc>
148{
149};
150
151template<>
152struct handler<nested_ex, no_alloc>
153 : nested_ex
154{
155};
156
157template<>
158struct handler<intrusive_ex, no_alloc>
159{
160};
161
162} // (anon)
163
164} // beast
165} // boost
166
167namespace boost {
168namespace asio {
169
170template<class Allocator>
171struct associated_allocator<
172 boost::beast::handler<
173 boost::beast::no_ex,
174 boost::beast::intrusive_alloc>,
175 Allocator>
176{
177 using type =
178 boost::beast::intrusive_alloc::allocator_type;
179
180 static type get(
181 boost::beast::handler<
182 boost::beast::no_ex,
183 boost::beast::intrusive_alloc> const&,
184 Allocator const& = Allocator()) noexcept
185 {
186 return type{};
187 }
188};
189
190template<class Executor>
191struct associated_executor<
192 boost::beast::handler<
193 boost::beast::intrusive_ex,
194 boost::beast::no_alloc>,
195 Executor>
196{
197 using type =
198 boost::beast::intrusive_ex::executor_type;
199
200 static type get(
201 boost::beast::handler<
202 boost::beast::intrusive_ex,
203 boost::beast::no_alloc> const&,
204 Executor const& = Executor()) noexcept
205 {
206 return type{};
207 }
208};
209
210} // asio
211} // boost
212
213//------------------------------------------------------------------------------
214
215namespace boost {
216namespace beast {
217
218class async_base_test : public beast::unit_test::suite
219{
220public:
221 // no associated allocator
222
223 BOOST_STATIC_ASSERT(
224 std::is_same<
225 std::allocator<void>,
226 net::associated_allocator_t<
227 async_base<
228 handler<no_ex, no_alloc>,
229 net::io_context::executor_type>
230 >>::value);
231
232 BOOST_STATIC_ASSERT(
233 std::is_same<
234 std::allocator<int>,
235 net::associated_allocator_t<
236 async_base<
237 handler<no_ex, no_alloc>,
238 net::io_context::executor_type,
239 std::allocator<int>>
240 >>::value);
241
242 BOOST_STATIC_ASSERT(
243 std::is_same<
244 std::allocator<void>,
245 net::associated_allocator_t<
246 async_base<
247 handler<no_ex, no_alloc>,
248 net::io_context::executor_type>,
249 std::allocator<int> // ignored
250 >>::value);
251
252 BOOST_STATIC_ASSERT(
253 std::is_same<
254 std::allocator<int>,
255 net::associated_allocator_t<
256 async_base<
257 handler<no_ex, no_alloc>,
258 net::io_context::executor_type,
259 std::allocator<int>>,
260 std::allocator<double> // ignored
261 >>::value);
262
263 // nested associated allocator
264
265 BOOST_STATIC_ASSERT(
266 std::is_same<
267 nested_alloc::allocator_type,
268 net::associated_allocator_t<
269 async_base<
270 handler<no_ex, nested_alloc>,
271 net::io_context::executor_type>
272 >>::value);
273
274 BOOST_STATIC_ASSERT(
275 std::is_same<
276 nested_alloc::allocator_type,
277 net::associated_allocator_t<
278 async_base<
279 handler<no_ex, nested_alloc>,
280 net::io_context::executor_type,
281 std::allocator<int>> // ignored
282 >>::value);
283
284 BOOST_STATIC_ASSERT(
285 std::is_same<
286 nested_alloc::allocator_type,
287 net::associated_allocator_t<
288 async_base<
289 handler<no_ex, nested_alloc>,
290 net::io_context::executor_type>,
291 std::allocator<int> // ignored
292 >>::value);
293
294 BOOST_STATIC_ASSERT(
295 std::is_same<
296 nested_alloc::allocator_type,
297 net::associated_allocator_t<
298 async_base<
299 handler<no_ex, nested_alloc>,
300 net::io_context::executor_type,
301 std::allocator<int>>, // ignored
302 std::allocator<int> // ignored
303 >>::value);
304
305 // intrusive associated allocator
306
307 BOOST_STATIC_ASSERT(
308 std::is_same<
309 intrusive_alloc::allocator_type,
310 net::associated_allocator_t<
311 async_base<
312 handler<no_ex, intrusive_alloc>,
313 net::io_context::executor_type>
314 >>::value);
315
316 BOOST_STATIC_ASSERT(
317 std::is_same<
318 intrusive_alloc::allocator_type,
319 net::associated_allocator_t<
320 async_base<
321 handler<no_ex, intrusive_alloc>,
322 net::io_context::executor_type,
323 std::allocator<int>> // ignored
324 >>::value);
325
326 BOOST_STATIC_ASSERT(
327 std::is_same<
328 intrusive_alloc::allocator_type,
329 net::associated_allocator_t<
330 async_base<
331 handler<no_ex, intrusive_alloc>,
332 net::io_context::executor_type>,
333 std::allocator<int> // ignored
334 >>::value);
335
336 BOOST_STATIC_ASSERT(
337 std::is_same<
338 intrusive_alloc::allocator_type,
339 net::associated_allocator_t<
340 async_base<
341 handler<no_ex, intrusive_alloc>,
342 net::io_context::executor_type,
343 std::allocator<int>>, // ignored
344 std::allocator<int> // ignored
345 >>::value);
346
347 // no associated executor
348
349 BOOST_STATIC_ASSERT(
350 std::is_same<
351 ex1_type,
352 net::associated_executor_t<
353 async_base<
354 handler<no_ex, no_alloc>,
355 ex1_type>
356 >>::value);
357
358 BOOST_STATIC_ASSERT(
359 std::is_same<
360 ex1_type,
361 net::associated_executor_t<
362 async_base<
363 handler<no_ex, no_alloc>,
364 ex1_type>,
365 net::system_executor // ignored
366 >>::value);
367
368 // nested associated executor
369
370 BOOST_STATIC_ASSERT(
371 std::is_same<
372 nested_ex::executor_type,
373 net::associated_executor_t<
374 async_base<
375 handler<nested_ex, no_alloc>,
376 ex1_type>
377 >>::value);
378
379 BOOST_STATIC_ASSERT(
380 std::is_same<
381 nested_ex::executor_type,
382 net::associated_executor_t<
383 async_base<
384 handler<nested_ex, no_alloc>,
385 ex1_type>,
386 net::system_executor // ignored
387 >>::value);
388
389 // intrusive associated executor
390
391 BOOST_STATIC_ASSERT(
392 std::is_same<
393 intrusive_ex::executor_type,
394 net::associated_executor_t<
395 async_base<
396 handler<intrusive_ex, no_alloc>,
397 ex1_type>
398 >>::value);
399
400 BOOST_STATIC_ASSERT(
401 std::is_same<
402 intrusive_ex::executor_type,
403 net::associated_executor_t<
404 async_base<
405 handler<intrusive_ex, no_alloc>,
406 ex1_type>,
407 net::system_executor // ignored
408 >>::value);
409
410 struct final_handler
411 {
412 bool& invoked;
413
414 void
415 operator()()
416 {
417 invoked = true;
418 }
419 };
420
421 void
422 testBase()
423 {
424 // get_allocator
425 {
426 simple_allocator alloc;
427 simple_allocator alloc2;
428 async_base<
429 move_only_handler,
430 simple_executor,
431 simple_allocator> op(
432 move_only_handler{}, {}, alloc);
433 BEAST_EXPECT(op.get_allocator() == alloc);
434 BEAST_EXPECT(op.get_allocator() != alloc2);
435 }
436
437 // get_executor
438 {
439 simple_executor ex;
440 simple_executor ex2;
441 async_base<
442 move_only_handler,
443 simple_executor> op(
444 move_only_handler{}, ex);
445 BEAST_EXPECT(op.get_executor() == ex);
446 BEAST_EXPECT(op.get_executor() != ex2);
447 }
448
449 // move construction
450 {
451 async_base<
452 move_only_handler,
453 simple_executor> op(
454 move_only_handler{}, {});
455 auto op2 = std::move(op);
456 }
457
458 // observers
459 {
460 bool b = false;
461 async_base<
462 legacy_handler,
463 simple_executor> op(
464 legacy_handler{b}, {});
465 BEAST_EXPECT(! op.handler().hook_invoked);
466 b = true;
467 BEAST_EXPECT(op.handler().hook_invoked);
468 b = false;
469 BEAST_EXPECT(! op.release_handler().hook_invoked);
470 }
471
472 // invocation
473 {
474 net::io_context ioc;
475 async_base<
476 test::handler,
477 net::io_context::executor_type> op(
478 test::any_handler(), ioc.get_executor());
479 op.complete(true);
480 }
481 {
482 net::io_context ioc;
20effc67
TL
483 auto op = new
484 async_base<
485 test::handler,
486 net::io_context::executor_type>(
487 test::any_handler(), ioc.get_executor());
488 op->complete(false);
489 delete op;
92f5a8d4
TL
490 ioc.run();
491 }
492 {
493 async_base<
494 test::handler,
495 simple_executor> op(
496 test::any_handler(), {});
497 op.complete_now();
498 }
499
500 // legacy hooks
501 legacy_handler::test(
502 [](legacy_handler h)
503 {
504 return async_base<
505 legacy_handler,
506 simple_executor>(
507 std::move(h), {});
508 });
509 }
510
511 void
512 testStableBase()
513 {
514 // get_allocator
515 {
516 simple_allocator alloc;
517 simple_allocator alloc2;
518 stable_async_base<
519 move_only_handler,
520 simple_executor,
521 simple_allocator> op(
522 move_only_handler{}, {}, alloc);
523 BEAST_EXPECT(op.get_allocator() == alloc);
524 BEAST_EXPECT(op.get_allocator() != alloc2);
525 }
526
527 // get_executor
528 {
529 simple_executor ex;
530 simple_executor ex2;
531 stable_async_base<
532 move_only_handler,
533 simple_executor> op(
534 move_only_handler{}, ex);
535 BEAST_EXPECT(op.get_executor() == ex);
536 BEAST_EXPECT(op.get_executor() != ex2);
537 }
538
539 // move construction
540 {
541 stable_async_base<
542 move_only_handler,
543 simple_executor> op(
544 move_only_handler{}, {});
545 auto op2 = std::move(op);
546 }
547
548 // invocation
549 {
550 net::io_context ioc;
551 stable_async_base<
552 test::handler,
553 net::io_context::executor_type> op(
554 test::any_handler(), ioc.get_executor());
555 op.complete(true);
556 }
557 {
558 net::io_context ioc1;
559 net::io_context ioc2;
560 auto h = net::bind_executor(ioc2, test::any_handler());
20effc67 561 auto op = new stable_async_base<
92f5a8d4 562 decltype(h),
20effc67 563 net::io_context::executor_type>(
92f5a8d4
TL
564 std::move(h),
565 ioc1.get_executor());
20effc67
TL
566 op->complete(false);
567 delete op;
92f5a8d4
TL
568 BEAST_EXPECT(ioc1.run() == 0);
569 BEAST_EXPECT(ioc2.run() == 1);
570 }
571 {
572 stable_async_base<
573 test::handler,
574 simple_executor> op(
575 test::any_handler(), {});
576 op.complete_now();
577 }
578
579 // legacy hooks
580 legacy_handler::test(
581 [](legacy_handler h)
582 {
583 return stable_async_base<
584 legacy_handler,
585 simple_executor>(
586 std::move(h), {});
587 });
588
589 // allocate_stable
590
591 {
592 bool destroyed = false;
593 {
594 struct data
595 {
596 bool& destroyed;
597
598 ~data()
599 {
600 destroyed = true;
601 }
602 };
603 stable_async_base<
604 move_only_handler,
605 simple_executor> op(
606 move_only_handler{}, {});
607 BEAST_EXPECT(! destroyed);
608 auto& d = allocate_stable<data>(op, destroyed);
609 BEAST_EXPECT(! d.destroyed);
610 }
611 BEAST_EXPECT(destroyed);
612 }
613 {
614 struct throwing_data
615 {
616 throwing_data()
617 {
618 BOOST_THROW_EXCEPTION(
619 std::exception{});
620 }
621 };
622 stable_async_base<
623 move_only_handler,
624 simple_executor> op(
625 move_only_handler{}, {});
626 try
627 {
628 allocate_stable<throwing_data>(op);
629 fail();
630 }
631 catch(std::exception const&)
632 {
633 pass();
634 }
635 }
636 }
637
638 //--------------------------------------------------------------------------
639
640 // Asynchronously read into a buffer until the buffer is full, or an error occurs
20effc67 641 template<class AsyncReadStream, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
92f5a8d4
TL
642 typename net::async_result<ReadHandler, void(error_code, std::size_t)>::return_type
643 async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
644 {
645 using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
646 using base_type = async_base<handler_type, typename AsyncReadStream::executor_type>;
647
648 struct op : base_type
649 {
650 AsyncReadStream& stream_;
651 net::mutable_buffer buffer_;
652 std::size_t total_bytes_transferred_;
653
654 op(
655 AsyncReadStream& stream,
656 net::mutable_buffer buffer,
657 handler_type& handler)
658 : base_type(std::move(handler), stream.get_executor())
659 , stream_(stream)
660 , buffer_(buffer)
661 , total_bytes_transferred_(0)
662 {
663 (*this)({}, 0, false); // start the operation
664 }
665
666 void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
667 {
668 // Adjust the count of bytes and advance our buffer
669 total_bytes_transferred_ += bytes_transferred;
670 buffer_ = buffer_ + bytes_transferred;
671
672 // Keep reading until buffer is full or an error occurs
673 if(! ec && buffer_.size() > 0)
674 return stream_.async_read_some(buffer_, std::move(*this));
675
676 // Call the completion handler with the result. If `is_continuation` is
677 // false, which happens on the first time through this function, then
678 // `net::post` will be used to call the completion handler, otherwise
679 // the completion handler will be invoked directly.
680
681 this->complete(is_continuation, ec, total_bytes_transferred_);
682 }
683 };
684
685 net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
686 op(stream, buffer, init.completion_handler);
687 return init.result.get();
688 }
689
690 // Asynchronously send a message multiple times, once per second
691 template <class AsyncWriteStream, class T, class WriteHandler>
692 auto async_write_messages(
693 AsyncWriteStream& stream,
694 T const& message,
695 std::size_t repeat_count,
696 WriteHandler&& handler) ->
697 typename net::async_result<
698 typename std::decay<WriteHandler>::type,
699 void(error_code)>::return_type
700 {
701 using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
702 using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
703
704 struct op : base_type, boost::asio::coroutine
705 {
706 // This object must have a stable address
707 struct temporary_data
708 {
709 // Although std::string is in theory movable, most implementations
710 // use a "small buffer optimization" which means that we might
711 // be submitting a buffer to the write operation and then
712 // moving the string, invalidating the buffer. To prevent
713 // undefined behavior we store the string object itself at
714 // a stable location.
715 std::string const message;
716
717 net::steady_timer timer;
718
719 temporary_data(std::string message_, net::io_context& ctx)
720 : message(std::move(message_))
721 , timer(ctx)
722 {
723 }
724 };
725
726 AsyncWriteStream& stream_;
727 std::size_t repeats_;
728 temporary_data& data_;
729
730 op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
731 : base_type(std::move(handler), stream.get_executor())
732 , stream_(stream)
733 , repeats_(repeats)
20effc67
TL
734 , data_(allocate_stable<temporary_data>(*this,
735 std::move(message),
736 net::query(stream.get_executor(), net::execution::context)))
92f5a8d4
TL
737 {
738 (*this)(); // start the operation
739 }
740
741 // Including this file provides the keywords for macro-based coroutines
742 #include <boost/asio/yield.hpp>
743
744 void operator()(error_code ec = {}, std::size_t = 0)
745 {
746 reenter(*this)
747 {
748 // If repeats starts at 0 then we must complete immediately. But
749 // we can't call the final handler from inside the initiating
750 // function, so we post our intermediate handler first. We use
751 // net::async_write with an empty buffer instead of calling
752 // net::post to avoid an extra function template instantiation, to
753 // keep compile times lower and make the resulting executable smaller.
754 yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
755 while(! ec && repeats_-- > 0)
756 {
757 // Send the string. We construct a `const_buffer` here to guarantee
758 // that we do not create an additional function template instantation
759 // of net::async_write, since we already instantiated it above for
760 // net::const_buffer.
761
762 yield net::async_write(stream_,
763 net::const_buffer(net::buffer(data_.message)), std::move(*this));
764 if(ec)
765 break;
766
767 // Set the timer and wait
768 data_.timer.expires_after(std::chrono::seconds(1));
769 yield data_.timer.async_wait(std::move(*this));
770 }
771 }
772
773 // The base class destroys the temporary data automatically,
774 // before invoking the final completion handler
775 this->complete_now(ec);
776 }
777
778 // Including this file undefines the macros for the coroutines
779 #include <boost/asio/unyield.hpp>
780 };
781
782 net::async_completion<WriteHandler, void(error_code)> completion(handler);
783 std::ostringstream os;
784 os << message;
785 op(stream, repeat_count, os.str(), completion.completion_handler);
786 return completion.result.get();
787 }
788
789 void
790 testJavadocs()
791 {
792 struct handler
793 {
794 void operator()(error_code = {}, std::size_t = 0)
795 {
796 }
797 };
798
799 BEAST_EXPECT((&async_base_test::async_read<test::stream, handler>));
800 BEAST_EXPECT((&async_base_test::async_write_messages<test::stream, std::string, handler>));
801 }
802
803 //--------------------------------------------------------------------------
804
805 void
806 run() override
807 {
808 testBase();
809 testStableBase();
810 testJavadocs();
811 }
812};
813
814BEAST_DEFINE_TESTSUITE(beast,core,async_base);
815
816} // beast
817} // boost