]>
Commit | Line | Data |
---|---|---|
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 | ||
31 | namespace boost { | |
32 | namespace beast { | |
33 | ||
34 | namespace { | |
35 | ||
20effc67 TL |
36 | #if defined(BOOST_ASIO_NO_TS_EXECUTORS) |
37 | struct 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 | }; | |
80 | BOOST_STATIC_ASSERT(net::execution::is_executor<ex1_type>::value); | |
81 | #else | |
92f5a8d4 TL |
82 | struct 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 |
91 | BOOST_STATIC_ASSERT(net::is_executor<ex1_type>::value); |
92 | #endif | |
93 | ||
92f5a8d4 TL |
94 | |
95 | struct no_alloc | |
96 | { | |
97 | }; | |
98 | ||
99 | struct nested_alloc | |
100 | { | |
101 | struct allocator_type | |
102 | { | |
103 | }; | |
104 | }; | |
105 | ||
106 | struct intrusive_alloc | |
107 | { | |
108 | struct allocator_type | |
109 | { | |
110 | }; | |
111 | }; | |
112 | ||
113 | struct no_ex | |
114 | { | |
115 | using executor_type = net::system_executor; | |
116 | }; | |
117 | ||
118 | struct nested_ex | |
119 | { | |
120 | struct executor_type | |
121 | { | |
122 | }; | |
123 | }; | |
124 | ||
125 | struct intrusive_ex | |
126 | { | |
127 | struct executor_type | |
128 | { | |
129 | }; | |
130 | }; | |
131 | ||
132 | template<class E, class A> | |
133 | struct handler; | |
134 | ||
135 | template<> | |
136 | struct handler<no_ex, no_alloc> | |
137 | { | |
138 | }; | |
139 | ||
140 | template<> | |
141 | struct handler<no_ex, nested_alloc> | |
142 | : nested_alloc | |
143 | { | |
144 | }; | |
145 | ||
146 | template<> | |
147 | struct handler<no_ex, intrusive_alloc> | |
148 | { | |
149 | }; | |
150 | ||
151 | template<> | |
152 | struct handler<nested_ex, no_alloc> | |
153 | : nested_ex | |
154 | { | |
155 | }; | |
156 | ||
157 | template<> | |
158 | struct handler<intrusive_ex, no_alloc> | |
159 | { | |
160 | }; | |
161 | ||
162 | } // (anon) | |
163 | ||
164 | } // beast | |
165 | } // boost | |
166 | ||
167 | namespace boost { | |
168 | namespace asio { | |
169 | ||
170 | template<class Allocator> | |
171 | struct 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 | ||
190 | template<class Executor> | |
191 | struct 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 | ||
215 | namespace boost { | |
216 | namespace beast { | |
217 | ||
218 | class async_base_test : public beast::unit_test::suite | |
219 | { | |
220 | public: | |
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 | ||
814 | BEAST_DEFINE_TESTSUITE(beast,core,async_base); | |
815 | ||
816 | } // beast | |
817 | } // boost |