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