2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 // Test that header file is self-contained.
11 #include <boost/beast/core/async_base.hpp>
13 #include "test_handler.hpp"
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>
28 //------------------------------------------------------------------------------
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&&) {}
56 struct intrusive_alloc
65 using executor_type = net::system_executor;
82 template<class E, class A>
86 struct handler<no_ex, no_alloc>
91 struct handler<no_ex, nested_alloc>
97 struct handler<no_ex, intrusive_alloc>
102 struct handler<nested_ex, no_alloc>
108 struct handler<intrusive_ex, no_alloc>
120 template<class Allocator>
121 struct associated_allocator<
122 boost::beast::handler<
124 boost::beast::intrusive_alloc>,
128 boost::beast::intrusive_alloc::allocator_type;
131 boost::beast::handler<
133 boost::beast::intrusive_alloc> const&,
134 Allocator const& = Allocator()) noexcept
140 template<class Executor>
141 struct associated_executor<
142 boost::beast::handler<
143 boost::beast::intrusive_ex,
144 boost::beast::no_alloc>,
148 boost::beast::intrusive_ex::executor_type;
151 boost::beast::handler<
152 boost::beast::intrusive_ex,
153 boost::beast::no_alloc> const&,
154 Executor const& = Executor()) noexcept
163 //------------------------------------------------------------------------------
168 class async_base_test : public beast::unit_test::suite
171 // no associated allocator
175 std::allocator<void>,
176 net::associated_allocator_t<
178 handler<no_ex, no_alloc>,
179 net::io_context::executor_type>
185 net::associated_allocator_t<
187 handler<no_ex, no_alloc>,
188 net::io_context::executor_type,
194 std::allocator<void>,
195 net::associated_allocator_t<
197 handler<no_ex, no_alloc>,
198 net::io_context::executor_type>,
199 std::allocator<int> // ignored
205 net::associated_allocator_t<
207 handler<no_ex, no_alloc>,
208 net::io_context::executor_type,
209 std::allocator<int>>,
210 std::allocator<double> // ignored
213 // nested associated allocator
217 nested_alloc::allocator_type,
218 net::associated_allocator_t<
220 handler<no_ex, nested_alloc>,
221 net::io_context::executor_type>
226 nested_alloc::allocator_type,
227 net::associated_allocator_t<
229 handler<no_ex, nested_alloc>,
230 net::io_context::executor_type,
231 std::allocator<int>> // ignored
236 nested_alloc::allocator_type,
237 net::associated_allocator_t<
239 handler<no_ex, nested_alloc>,
240 net::io_context::executor_type>,
241 std::allocator<int> // ignored
246 nested_alloc::allocator_type,
247 net::associated_allocator_t<
249 handler<no_ex, nested_alloc>,
250 net::io_context::executor_type,
251 std::allocator<int>>, // ignored
252 std::allocator<int> // ignored
255 // intrusive associated allocator
259 intrusive_alloc::allocator_type,
260 net::associated_allocator_t<
262 handler<no_ex, intrusive_alloc>,
263 net::io_context::executor_type>
268 intrusive_alloc::allocator_type,
269 net::associated_allocator_t<
271 handler<no_ex, intrusive_alloc>,
272 net::io_context::executor_type,
273 std::allocator<int>> // ignored
278 intrusive_alloc::allocator_type,
279 net::associated_allocator_t<
281 handler<no_ex, intrusive_alloc>,
282 net::io_context::executor_type>,
283 std::allocator<int> // ignored
288 intrusive_alloc::allocator_type,
289 net::associated_allocator_t<
291 handler<no_ex, intrusive_alloc>,
292 net::io_context::executor_type,
293 std::allocator<int>>, // ignored
294 std::allocator<int> // ignored
297 // no associated executor
302 net::associated_executor_t<
304 handler<no_ex, no_alloc>,
311 net::associated_executor_t<
313 handler<no_ex, no_alloc>,
315 net::system_executor // ignored
318 // nested associated executor
322 nested_ex::executor_type,
323 net::associated_executor_t<
325 handler<nested_ex, no_alloc>,
331 nested_ex::executor_type,
332 net::associated_executor_t<
334 handler<nested_ex, no_alloc>,
336 net::system_executor // ignored
339 // intrusive associated executor
343 intrusive_ex::executor_type,
344 net::associated_executor_t<
346 handler<intrusive_ex, no_alloc>,
352 intrusive_ex::executor_type,
353 net::associated_executor_t<
355 handler<intrusive_ex, no_alloc>,
357 net::system_executor // ignored
376 simple_allocator alloc;
377 simple_allocator alloc2;
381 simple_allocator> op(
382 move_only_handler{}, {}, alloc);
383 BEAST_EXPECT(op.get_allocator() == alloc);
384 BEAST_EXPECT(op.get_allocator() != alloc2);
394 move_only_handler{}, ex);
395 BEAST_EXPECT(op.get_executor() == ex);
396 BEAST_EXPECT(op.get_executor() != ex2);
404 move_only_handler{}, {});
405 auto op2 = std::move(op);
414 legacy_handler{b}, {});
415 BEAST_EXPECT(! op.handler().hook_invoked);
417 BEAST_EXPECT(op.handler().hook_invoked);
419 BEAST_EXPECT(! op.release_handler().hook_invoked);
427 net::io_context::executor_type> op(
428 test::any_handler(), ioc.get_executor());
435 net::io_context::executor_type> op(
436 test::any_handler(), ioc.get_executor());
444 test::any_handler(), {});
449 legacy_handler::test(
464 simple_allocator alloc;
465 simple_allocator alloc2;
469 simple_allocator> op(
470 move_only_handler{}, {}, alloc);
471 BEAST_EXPECT(op.get_allocator() == alloc);
472 BEAST_EXPECT(op.get_allocator() != alloc2);
482 move_only_handler{}, ex);
483 BEAST_EXPECT(op.get_executor() == ex);
484 BEAST_EXPECT(op.get_executor() != ex2);
492 move_only_handler{}, {});
493 auto op2 = std::move(op);
501 net::io_context::executor_type> op(
502 test::any_handler(), ioc.get_executor());
506 net::io_context ioc1;
507 net::io_context ioc2;
508 auto h = net::bind_executor(ioc2, test::any_handler());
511 net::io_context::executor_type> op(
513 ioc1.get_executor());
515 BEAST_EXPECT(ioc1.run() == 0);
516 BEAST_EXPECT(ioc2.run() == 1);
522 test::any_handler(), {});
527 legacy_handler::test(
530 return stable_async_base<
539 bool destroyed = false;
553 move_only_handler{}, {});
554 BEAST_EXPECT(! destroyed);
555 auto& d = allocate_stable<data>(op, destroyed);
556 BEAST_EXPECT(! d.destroyed);
558 BEAST_EXPECT(destroyed);
565 BOOST_THROW_EXCEPTION(
572 move_only_handler{}, {});
575 allocate_stable<throwing_data>(op);
578 catch(std::exception const&)
585 //--------------------------------------------------------------------------
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)
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>;
595 struct op : base_type
597 AsyncReadStream& stream_;
598 net::mutable_buffer buffer_;
599 std::size_t total_bytes_transferred_;
602 AsyncReadStream& stream,
603 net::mutable_buffer buffer,
604 handler_type& handler)
605 : base_type(std::move(handler), stream.get_executor())
608 , total_bytes_transferred_(0)
610 (*this)({}, 0, false); // start the operation
613 void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
615 // Adjust the count of bytes and advance our buffer
616 total_bytes_transferred_ += bytes_transferred;
617 buffer_ = buffer_ + bytes_transferred;
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));
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.
628 this->complete(is_continuation, ec, total_bytes_transferred_);
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();
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,
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
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>;
651 struct op : base_type, boost::asio::coroutine
653 // This object must have a stable address
654 struct temporary_data
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;
664 net::steady_timer timer;
666 temporary_data(std::string message_, net::io_context& ctx)
667 : message(std::move(message_))
673 AsyncWriteStream& stream_;
674 std::size_t repeats_;
675 temporary_data& data_;
677 op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
678 : base_type(std::move(handler), stream.get_executor())
681 , data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
683 (*this)(); // start the operation
686 // Including this file provides the keywords for macro-based coroutines
687 #include <boost/asio/yield.hpp>
689 void operator()(error_code ec = {}, std::size_t = 0)
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)
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.
707 yield net::async_write(stream_,
708 net::const_buffer(net::buffer(data_.message)), std::move(*this));
712 // Set the timer and wait
713 data_.timer.expires_after(std::chrono::seconds(1));
714 yield data_.timer.async_wait(std::move(*this));
718 // The base class destroys the temporary data automatically,
719 // before invoking the final completion handler
720 this->complete_now(ec);
723 // Including this file undefines the macros for the coroutines
724 #include <boost/asio/unyield.hpp>
727 net::async_completion<WriteHandler, void(error_code)> completion(handler);
728 std::ostringstream os;
730 op(stream, repeat_count, os.str(), completion.completion_handler);
731 return completion.result.get();
739 void operator()(error_code = {}, std::size_t = 0)
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>));
748 //--------------------------------------------------------------------------
759 BEAST_DEFINE_TESTSUITE(beast,core,async_base);