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>
29 //------------------------------------------------------------------------------
36 #if defined(BOOST_ASIO_NO_TS_EXECUTORS)
40 net::execution_context
&
41 query(net::execution::context_t c
) const noexcept
42 { return *reinterpret_cast<net::execution_context
*>(0); }
44 net::execution::blocking_t
45 query(net::execution::blocking_t
) const noexcept
46 { return net::execution::blocking
; };
48 net::execution::outstanding_work_t
49 query(net::execution::outstanding_work_t w
) const noexcept
50 { return net::execution::outstanding_work
; }
53 require(net::execution::blocking_t::possibly_t b
) const
57 require(net::execution::blocking_t::never_t b
) const
61 prefer(net::execution::outstanding_work_t::untracked_t w
) const
65 prefer(net::execution::outstanding_work_t::tracked_t w
) const
74 operator==(ex1_type
const &) const noexcept
77 operator!=(ex1_type
const &) const noexcept
80 BOOST_STATIC_ASSERT(net::execution::is_executor
<ex1_type
>::value
);
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
&&) {}
91 BOOST_STATIC_ASSERT(net::is_executor
<ex1_type
>::value
);
101 struct allocator_type
106 struct intrusive_alloc
108 struct allocator_type
115 using executor_type
= net::system_executor
;
132 template<class E
, class A
>
136 struct handler
<no_ex
, no_alloc
>
141 struct handler
<no_ex
, nested_alloc
>
147 struct handler
<no_ex
, intrusive_alloc
>
152 struct handler
<nested_ex
, no_alloc
>
158 struct handler
<intrusive_ex
, no_alloc
>
170 template<class Allocator
>
171 struct associated_allocator
<
172 boost::beast::handler
<
174 boost::beast::intrusive_alloc
>,
178 boost::beast::intrusive_alloc::allocator_type
;
181 boost::beast::handler
<
183 boost::beast::intrusive_alloc
> const&,
184 Allocator
const& = Allocator()) noexcept
190 template<class Executor
>
191 struct associated_executor
<
192 boost::beast::handler
<
193 boost::beast::intrusive_ex
,
194 boost::beast::no_alloc
>,
198 boost::beast::intrusive_ex::executor_type
;
201 boost::beast::handler
<
202 boost::beast::intrusive_ex
,
203 boost::beast::no_alloc
> const&,
204 Executor
const& = Executor()) noexcept
213 //------------------------------------------------------------------------------
218 class async_base_test
: public beast::unit_test::suite
221 // no associated allocator
225 std::allocator
<void>,
226 net::associated_allocator_t
<
228 handler
<no_ex
, no_alloc
>,
229 net::io_context::executor_type
>
235 net::associated_allocator_t
<
237 handler
<no_ex
, no_alloc
>,
238 net::io_context::executor_type
,
244 std::allocator
<void>,
245 net::associated_allocator_t
<
247 handler
<no_ex
, no_alloc
>,
248 net::io_context::executor_type
>,
249 std::allocator
<int> // ignored
255 net::associated_allocator_t
<
257 handler
<no_ex
, no_alloc
>,
258 net::io_context::executor_type
,
259 std::allocator
<int>>,
260 std::allocator
<double> // ignored
263 // nested associated allocator
267 nested_alloc::allocator_type
,
268 net::associated_allocator_t
<
270 handler
<no_ex
, nested_alloc
>,
271 net::io_context::executor_type
>
276 nested_alloc::allocator_type
,
277 net::associated_allocator_t
<
279 handler
<no_ex
, nested_alloc
>,
280 net::io_context::executor_type
,
281 std::allocator
<int>> // ignored
286 nested_alloc::allocator_type
,
287 net::associated_allocator_t
<
289 handler
<no_ex
, nested_alloc
>,
290 net::io_context::executor_type
>,
291 std::allocator
<int> // ignored
296 nested_alloc::allocator_type
,
297 net::associated_allocator_t
<
299 handler
<no_ex
, nested_alloc
>,
300 net::io_context::executor_type
,
301 std::allocator
<int>>, // ignored
302 std::allocator
<int> // ignored
305 // intrusive associated allocator
309 intrusive_alloc::allocator_type
,
310 net::associated_allocator_t
<
312 handler
<no_ex
, intrusive_alloc
>,
313 net::io_context::executor_type
>
318 intrusive_alloc::allocator_type
,
319 net::associated_allocator_t
<
321 handler
<no_ex
, intrusive_alloc
>,
322 net::io_context::executor_type
,
323 std::allocator
<int>> // ignored
328 intrusive_alloc::allocator_type
,
329 net::associated_allocator_t
<
331 handler
<no_ex
, intrusive_alloc
>,
332 net::io_context::executor_type
>,
333 std::allocator
<int> // ignored
338 intrusive_alloc::allocator_type
,
339 net::associated_allocator_t
<
341 handler
<no_ex
, intrusive_alloc
>,
342 net::io_context::executor_type
,
343 std::allocator
<int>>, // ignored
344 std::allocator
<int> // ignored
347 // no associated executor
352 net::associated_executor_t
<
354 handler
<no_ex
, no_alloc
>,
361 net::associated_executor_t
<
363 handler
<no_ex
, no_alloc
>,
365 net::system_executor
// ignored
368 // nested associated executor
372 nested_ex::executor_type
,
373 net::associated_executor_t
<
375 handler
<nested_ex
, no_alloc
>,
381 nested_ex::executor_type
,
382 net::associated_executor_t
<
384 handler
<nested_ex
, no_alloc
>,
386 net::system_executor
// ignored
389 // intrusive associated executor
393 intrusive_ex::executor_type
,
394 net::associated_executor_t
<
396 handler
<intrusive_ex
, no_alloc
>,
402 intrusive_ex::executor_type
,
403 net::associated_executor_t
<
405 handler
<intrusive_ex
, no_alloc
>,
407 net::system_executor
// ignored
426 simple_allocator alloc
;
427 simple_allocator alloc2
;
431 simple_allocator
> op(
432 move_only_handler
{}, {}, alloc
);
433 BEAST_EXPECT(op
.get_allocator() == alloc
);
434 BEAST_EXPECT(op
.get_allocator() != alloc2
);
444 move_only_handler
{}, ex
);
445 BEAST_EXPECT(op
.get_executor() == ex
);
446 BEAST_EXPECT(op
.get_executor() != ex2
);
454 move_only_handler
{}, {});
455 auto op2
= std::move(op
);
464 legacy_handler
{b
}, {});
465 BEAST_EXPECT(! op
.handler().hook_invoked
);
467 BEAST_EXPECT(op
.handler().hook_invoked
);
469 BEAST_EXPECT(! op
.release_handler().hook_invoked
);
477 net::io_context::executor_type
> op(
478 test::any_handler(), ioc
.get_executor());
486 net::io_context::executor_type
>(
487 test::any_handler(), ioc
.get_executor());
496 test::any_handler(), {});
501 legacy_handler::test(
516 simple_allocator alloc
;
517 simple_allocator alloc2
;
521 simple_allocator
> op(
522 move_only_handler
{}, {}, alloc
);
523 BEAST_EXPECT(op
.get_allocator() == alloc
);
524 BEAST_EXPECT(op
.get_allocator() != alloc2
);
534 move_only_handler
{}, ex
);
535 BEAST_EXPECT(op
.get_executor() == ex
);
536 BEAST_EXPECT(op
.get_executor() != ex2
);
544 move_only_handler
{}, {});
545 auto op2
= std::move(op
);
553 net::io_context::executor_type
> op(
554 test::any_handler(), ioc
.get_executor());
558 net::io_context ioc1
;
559 net::io_context ioc2
;
560 auto h
= net::bind_executor(ioc2
, test::any_handler());
561 auto op
= new stable_async_base
<
563 net::io_context::executor_type
>(
565 ioc1
.get_executor());
568 BEAST_EXPECT(ioc1
.run() == 0);
569 BEAST_EXPECT(ioc2
.run() == 1);
575 test::any_handler(), {});
580 legacy_handler::test(
583 return stable_async_base
<
592 bool destroyed
= false;
606 move_only_handler
{}, {});
607 BEAST_EXPECT(! destroyed
);
608 auto& d
= allocate_stable
<data
>(op
, destroyed
);
609 BEAST_EXPECT(! d
.destroyed
);
611 BEAST_EXPECT(destroyed
);
618 BOOST_THROW_EXCEPTION(
625 move_only_handler
{}, {});
628 allocate_stable
<throwing_data
>(op
);
631 catch(std::exception
const&)
638 //--------------------------------------------------------------------------
640 // Asynchronously read into a buffer until the buffer is full, or an error occurs
641 template<class AsyncReadStream
, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler
>
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
)
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
>;
648 struct op
: base_type
650 AsyncReadStream
& stream_
;
651 net::mutable_buffer buffer_
;
652 std::size_t total_bytes_transferred_
;
655 AsyncReadStream
& stream
,
656 net::mutable_buffer buffer
,
657 handler_type
& handler
)
658 : base_type(std::move(handler
), stream
.get_executor())
661 , total_bytes_transferred_(0)
663 (*this)({}, 0, false); // start the operation
666 void operator()(error_code ec
, std::size_t bytes_transferred
, bool is_continuation
= true)
668 // Adjust the count of bytes and advance our buffer
669 total_bytes_transferred_
+= bytes_transferred
;
670 buffer_
= buffer_
+ bytes_transferred
;
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));
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.
681 this->complete(is_continuation
, ec
, total_bytes_transferred_
);
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();
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
,
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
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
>;
704 struct op
: base_type
, boost::asio::coroutine
706 // This object must have a stable address
707 struct temporary_data
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
;
717 net::steady_timer timer
;
719 temporary_data(std::string message_
, net::io_context
& ctx
)
720 : message(std::move(message_
))
726 AsyncWriteStream
& stream_
;
727 std::size_t repeats_
;
728 temporary_data
& data_
;
730 op(AsyncWriteStream
& stream
, std::size_t repeats
, std::string message
, handler_type
& handler
)
731 : base_type(std::move(handler
), stream
.get_executor())
734 , data_(allocate_stable
<temporary_data
>(*this,
736 net::query(stream
.get_executor(), net::execution::context
)))
738 (*this)(); // start the operation
741 // Including this file provides the keywords for macro-based coroutines
742 #include <boost/asio/yield.hpp>
744 void operator()(error_code ec
= {}, std::size_t = 0)
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)
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.
762 yield
net::async_write(stream_
,
763 net::const_buffer(net::buffer(data_
.message
)), std::move(*this));
767 // Set the timer and wait
768 data_
.timer
.expires_after(std::chrono::seconds(1));
769 yield data_
.timer
.async_wait(std::move(*this));
773 // The base class destroys the temporary data automatically,
774 // before invoking the final completion handler
775 this->complete_now(ec
);
778 // Including this file undefines the macros for the coroutines
779 #include <boost/asio/unyield.hpp>
782 net::async_completion
<WriteHandler
, void(error_code
)> completion(handler
);
783 std::ostringstream os
;
785 op(stream
, repeat_count
, os
.str(), completion
.completion_handler
);
786 return completion
.result
.get();
794 void operator()(error_code
= {}, std::size_t = 0)
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
>));
803 //--------------------------------------------------------------------------
814 BEAST_DEFINE_TESTSUITE(beast
,core
,async_base
);