Files
boost_beast/extras/beast/test/yield_to.hpp
T
Vinnie Falco 02d5aedc29 Refactor HTTP serialization and parsing (API Change):
fix #404

Parsing and serialization interfaces have been fine tuned and unified.

For parsing these stream operations are defined:

* read
* read_header
* read_some
* async_read
* async_read_header
* async_read_some

For serialization these stream operations are defined:

* write
* write_header
* write_some
* async_write
* async_write_header
* async_write_some
2017-07-20 08:12:17 -07:00

135 lines
2.8 KiB
C++

//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_TEST_YIELD_TO_HPP
#define BEAST_TEST_YIELD_TO_HPP
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/optional.hpp>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
namespace beast {
namespace test {
/** Mix-in to support tests using asio coroutines.
Derive from this class and use yield_to to launch test
functions inside coroutines. This is handy for testing
asynchronous asio code.
*/
class enable_yield_to
{
protected:
boost::asio::io_service ios_;
private:
boost::optional<boost::asio::io_service::work> work_;
std::vector<std::thread> threads_;
std::mutex m_;
std::condition_variable cv_;
std::size_t running_ = 0;
public:
/// The type of yield context passed to functions.
using yield_context =
boost::asio::yield_context;
explicit
enable_yield_to(std::size_t concurrency = 1)
: work_(ios_)
{
threads_.reserve(concurrency);
while(concurrency--)
threads_.emplace_back(
[&]{ ios_.run(); });
}
~enable_yield_to()
{
work_ = boost::none;
for(auto& t : threads_)
t.join();
}
/// Return the `io_service` associated with the object
boost::asio::io_service&
get_io_service()
{
return ios_;
}
/** Run one or more functions, each in a coroutine.
This call will block until all coroutines terminate.
Each functions should have this signature:
@code
void f(yield_context);
@endcode
@param fn... One or more functions to invoke.
*/
#if BEAST_DOXYGEN
template<class... FN>
void
yield_to(FN&&... fn)
#else
template<class F0, class... FN>
void
yield_to(F0&& f0, FN&&... fn);
#endif
private:
void
spawn()
{
}
template<class F0, class... FN>
void
spawn(F0&& f, FN&&... fn);
};
template<class F0, class... FN>
void
enable_yield_to::
yield_to(F0&& f0, FN&&... fn)
{
running_ = 1 + sizeof...(FN);
spawn(f0, fn...);
std::unique_lock<std::mutex> lock{m_};
cv_.wait(lock, [&]{ return running_ == 0; });
}
template<class F0, class... FN>
inline
void
enable_yield_to::
spawn(F0&& f, FN&&... fn)
{
boost::asio::spawn(ios_,
[&](yield_context yield)
{
f(yield);
std::lock_guard<std::mutex> lock{m_};
if(--running_ == 0)
cv_.notify_all();
}
, boost::coroutines::attributes(2 * 1024 * 1024));
spawn(fn...);
}
} // test
} // beast
#endif