Skip to content

Commit

Permalink
Pools can now be constructed from executors
Browse files Browse the repository at this point in the history
close #222
  • Loading branch information
anarthal committed Mar 5, 2024
1 parent f98427c commit 6b85294
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 113 deletions.
73 changes: 68 additions & 5 deletions include/boost/mysql/connection_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ class connection_pool
{
std::shared_ptr<detail::pool_impl> impl_;

#ifndef BOOST_MYSQL_DOXYGEN
friend struct detail::access;
#endif

static constexpr std::chrono::steady_clock::duration get_default_timeout() noexcept
{
return std::chrono::seconds(30);
Expand Down Expand Up @@ -322,13 +326,16 @@ class connection_pool
);
}

BOOST_MYSQL_DECL
connection_pool(pool_executor_params&& ex_params, pool_params&& params, int);

public:
/**
* \brief Constructs a connection pool.
* \details
* Internal I/O objects (like timers and channels) are constructed using
* \ref pool_executor_params::pool_executor on `ex_params`. Connections are constructed using
* \ref pool_executor_params::connection_executor. This can be used to create
* Internal I/O objects (like timers) are constructed using
* `ex_params.pool_executor`. Connections are constructed using
* `ex_params.connection_executor`. This can be used to create
* thread-safe pools.
* \n
* The pool is created in a "not-running" state. Call \ref async_run to transition to the
Expand All @@ -342,8 +349,64 @@ class connection_pool
* \throws std::invalid_argument If `params` contains values that violate the rules described in \ref
* pool_params.
*/
BOOST_MYSQL_DECL
connection_pool(const pool_executor_params& ex_params, pool_params params);
connection_pool(pool_executor_params ex_params, pool_params params)
: connection_pool(std::move(ex_params), std::move(params), 0)
{
}

/**
* \brief Constructs a connection pool.
* \details
* Both internal I/O objects and connections are constructed using the passed executor.
* \n
* The pool is created in a "not-running" state. Call \ref async_run to transition to the
* "running" state. Calling \ref async_get_connection in the "not-running" state will fail
* with \ref client_errc::cancelled.
* \n
* The constructed pool is always valid (`this->valid() == true`).
*
* \par Exception safety
* Strong guarantee. Exceptions may be thrown by memory allocations.
* \throws std::invalid_argument If `params` contains values that violate the rules described in \ref
* pool_params.
*/
connection_pool(asio::any_io_executor ex, pool_params params)
: connection_pool(pool_executor_params{ex, ex}, std::move(params), 0)
{
}

/**
* \brief Constructs a connection pool.
* \details
* Both internal I/O objects and connections are constructed using `ctx.get_executor()`.
* \n
* The pool is created in a "not-running" state. Call \ref async_run to transition to the
* "running" state. Calling \ref async_get_connection in the "not-running" state will fail
* with \ref client_errc::cancelled.
* \n
* The constructed pool is always valid (`this->valid() == true`).
* \n
* This function participates in overload resolution only if `ExecutionContext`
* satisfies the `ExecutionContext` requirements imposed by Boost.Asio.
*
* \par Exception safety
* Strong guarantee. Exceptions may be thrown by memory allocations.
* \throws std::invalid_argument If `params` contains values that violate the rules described in \ref
* pool_params.
*/
template <
class ExecutionContext
#ifndef BOOST_MYSQL_DOXYGEN
,
class = typename std::enable_if<std::is_convertible<
decltype(std::declval<ExecutionContext&>().get_executor()),
asio::any_io_executor>::value>::type
#endif
>
connection_pool(ExecutionContext& ctx, pool_params params)
: connection_pool({ctx.get_executor(), ctx.get_executor()}, std::move(params), 0)
{
}

#ifndef BOOST_MYSQL_DOXYGEN
connection_pool(const connection_pool&) = delete;
Expand Down
4 changes: 2 additions & 2 deletions include/boost/mysql/impl/connection_pool.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ boost::mysql::any_connection& boost::mysql::detail::get_connection(boost::mysql:
return node.connection();
}

boost::mysql::connection_pool::connection_pool(const pool_executor_params& ex_params, pool_params params)
: impl_(std::make_shared<detail::pool_impl>(ex_params, std::move(params)))
boost::mysql::connection_pool::connection_pool(pool_executor_params&& ex_params, pool_params&& params, int)
: impl_(std::make_shared<detail::pool_impl>(std::move(ex_params), std::move(params)))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,10 @@ class basic_pool_impl : public std::enable_shared_from_this<basic_pool_impl<IoTr
};

public:
basic_pool_impl(const pool_executor_params& ex_params, pool_params&& params)
basic_pool_impl(pool_executor_params&& ex_params, pool_params&& params)
: params_(make_internal_pool_params(std::move(params))),
ex_(ex_params.pool_executor()),
conn_ex_(ex_params.connection_executor()),
ex_(std::move(ex_params.pool_executor)),
conn_ex_(std::move(ex_params.connection_executor)),
wait_gp_(ex_),
cancel_timer_(ex_, (std::chrono::steady_clock::time_point::max)())
{
Expand Down Expand Up @@ -321,6 +321,7 @@ class basic_pool_impl : public std::enable_shared_from_this<basic_pool_impl<IoTr
std::list<node_type>& nodes() noexcept { return all_conns_; }
shared_state_type& shared_state() noexcept { return shared_st_; }
internal_pool_params& params() noexcept { return params_; }
asio::any_io_executor connection_ex() noexcept { return conn_ex_; }
};

} // namespace detail
Expand Down
70 changes: 6 additions & 64 deletions include/boost/mysql/pool_params.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,71 +39,13 @@ namespace mysql {
* This part of the API is experimental, and may change in successive
* releases without previous notice.
*/
class pool_executor_params
struct pool_executor_params
{
#ifndef BOOST_MYSQL_DOXYGEN
struct
{
asio::any_io_executor pool_ex;
asio::any_io_executor conn_ex;
} impl_;
#endif

public:
/**
* \brief Constructor from individual executors.
* \details
* The constructed object has `this->pool_executor() == pool_ex`.
* If `conn_ex` is a default-constructed executor (this is the default),
* then the pool's executor will be used for connections, too
* (`this->connection_executor() == pool_ex`).
* Otherwise, `this->pool_executor() == conn_ex`.
*
* \par Exception safety
* No-throw guarantee.
*/
pool_executor_params(asio::any_io_executor pool_ex, asio::any_io_executor conn_ex = {})
: impl_{std::move(pool_ex), std::move(conn_ex)}
{
if (!impl_.conn_ex)
impl_.conn_ex = impl_.pool_ex;
}

/**
* \brief Constructor from an execution context.
* \details
* Equivalent to `pool_executor_params(ctx.get_executor())`.
* Uses the same executor for the pool and for the connections.
*
* \par Exception safety
* Strong guaantee. Exceptions thrown by `ctx.get_executor()` are propagated.
*/
template <
class ExecutionContext
#ifndef BOOST_MYSQL_DOXYGEN
,
class = typename std::enable_if<std::is_convertible<
decltype(std::declval<ExecutionContext&>().get_executor()),
asio::any_io_executor>::value>::type
#endif
>
pool_executor_params(ExecutionContext& ctx) : pool_executor_params(ctx.get_executor())
{
}
/// The executor to be used by the pool's internal objects.
asio::any_io_executor pool_executor;

/**
* \brief Retrieves the executor to be used by the pool's internal objects.
* \par Exception safety
* No-throw guarantee.
*/
asio::any_io_executor pool_executor() const noexcept { return impl_.pool_ex; }

/**
* \brief Retrieves the executor to be used by connections created by the pool.
* \par Exception safety
* No-throw guarantee.
*/
asio::any_io_executor connection_executor() const noexcept { return impl_.conn_ex; }
/// The executor to be used by connections created by the pool.
asio::any_io_executor connection_executor;

/**
* \brief Creates a pool_executor_params object that makes pools thread-safe.
Expand All @@ -120,7 +62,7 @@ class pool_executor_params
*/
static pool_executor_params thread_safe(asio::any_io_executor ex)
{
return pool_executor_params(asio::make_strand(ex), ex);
return pool_executor_params{asio::make_strand(ex), ex};
}
};

Expand Down
2 changes: 1 addition & 1 deletion test/integration/test/connection_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(pool_executors, fixture)
BOOST_TEST((pool_ex != conn_ex));

// Create and run the pool
connection_pool pool(pool_executor_params(pool_ex, conn_ex), create_pool_params());
connection_pool pool(pool_executor_params{pool_ex, conn_ex}, create_pool_params());
pool_guard grd(&pool);
pool.async_run(check_err);

Expand Down
47 changes: 43 additions & 4 deletions test/unit/test/connection_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ BOOST_AUTO_TEST_SUITE(test_pooled_connection)
struct pooled_connection_fixture
{
asio::io_context ctx;
std::shared_ptr<detail::pool_impl> pool{std::make_shared<detail::pool_impl>(ctx, pool_params())};
std::shared_ptr<detail::pool_impl> pool{std::make_shared<detail::pool_impl>(
pool_executor_params{ctx.get_executor(), ctx.get_executor()},
pool_params{}
)};

std::unique_ptr<detail::connection_node> create_node()
{
Expand Down Expand Up @@ -191,12 +194,48 @@ BOOST_AUTO_TEST_SUITE(test_connection_pool)
struct pool_fixture
{
asio::io_context ctx;
connection_pool pool{ctx, pool_params()};
connection_pool pool{ctx, pool_params{}};
};

BOOST_FIXTURE_TEST_CASE(init_ctor, pool_fixture)
BOOST_AUTO_TEST_CASE(ctor_from_pool_executor_params)
{
// Freshly constructed pools are valid
// Construct
asio::io_context ctx1, ctx2;
connection_pool pool(pool_executor_params{ctx1.get_executor(), ctx2.get_executor()}, pool_params{});

// Executors are correct
BOOST_TEST((pool.get_executor() == ctx1.get_executor()));
BOOST_TEST((detail::access::get_impl(pool)->connection_ex() == ctx2.get_executor()));

// The pool is valid
BOOST_TEST(pool.valid());
}

BOOST_AUTO_TEST_CASE(ctor_from_executor)
{
// Construct
asio::io_context ctx;
connection_pool pool(ctx.get_executor(), pool_params{});

// Executors are correct
BOOST_TEST((pool.get_executor() == ctx.get_executor()));
BOOST_TEST((detail::access::get_impl(pool)->connection_ex() == ctx.get_executor()));

// The pool is valid
BOOST_TEST(pool.valid());
}

BOOST_AUTO_TEST_CASE(ctor_from_execution_context)
{
// Construct
asio::io_context ctx;
connection_pool pool(ctx, pool_params{});

// Executors are correct
BOOST_TEST((pool.get_executor() == ctx.get_executor()));
BOOST_TEST((detail::access::get_impl(pool)->connection_ex() == ctx.get_executor()));

// The pool is valid
BOOST_TEST(pool.valid());
}

Expand Down
5 changes: 4 additions & 1 deletion test/unit/test/connection_pool/connection_pool_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,10 @@ static void pool_test(boost::mysql::pool_params params, Args&&... args)
asio::io_context ctx;

// Pool (must be created using dynamic memory)
auto pool = std::make_shared<mock_pool>(ctx, std::move(params));
auto pool = std::make_shared<mock_pool>(
pool_executor_params{ctx.get_executor(), ctx.get_executor()},
std::move(params)
);

// This flag is only set to true after the test finishes.
// If the test timeouts, it will be false
Expand Down
37 changes: 4 additions & 33 deletions test/unit/test/pool_params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,17 @@
using namespace boost::mysql;
namespace asio = boost::asio;

BOOST_AUTO_TEST_SUITE(test_pool_executor_params)

BOOST_AUTO_TEST_CASE(ctor_from_one_executor)
{
asio::io_context ctx;
pool_executor_params params(ctx.get_executor());
BOOST_TEST((params.pool_executor() == ctx.get_executor()));
BOOST_TEST((params.connection_executor() == ctx.get_executor()));
}

BOOST_AUTO_TEST_CASE(ctor_from_two_executors)
{
asio::io_context ctx;
auto strand = asio::make_strand(ctx);
pool_executor_params params(ctx.get_executor(), strand);
BOOST_TEST((params.pool_executor() == ctx.get_executor()));
BOOST_TEST((params.connection_executor() == strand));
}

BOOST_AUTO_TEST_CASE(ctor_from_execution_context)
{
asio::io_context ctx;
pool_executor_params params(ctx);
BOOST_TEST((params.pool_executor() == ctx.get_executor()));
BOOST_TEST((params.connection_executor() == ctx.get_executor()));
}
BOOST_AUTO_TEST_SUITE(test_pool_params)

BOOST_AUTO_TEST_CASE(thread_safe)
BOOST_AUTO_TEST_CASE(pool_executor_params_thread_safe)
{
// The strand is only applied to the pool, and not to connections
asio::io_context ctx;
auto params = pool_executor_params::thread_safe(ctx.get_executor());
BOOST_TEST((params.pool_executor() != ctx.get_executor()));
BOOST_TEST((params.connection_executor() == ctx.get_executor()));
BOOST_TEST((params.pool_executor != ctx.get_executor()));
BOOST_TEST((params.connection_executor == ctx.get_executor()));
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(test_pool_params)

BOOST_AUTO_TEST_CASE(invalid_params)
{
struct
Expand Down

0 comments on commit 6b85294

Please sign in to comment.