Skip to content

Commit

Permalink
Add execution_context::service_maker abstract base class.
Browse files Browse the repository at this point in the history
A service_maker is an object that is passed to an execution context's
constructor, and allows services to be added at context construction
time. Additional constructor overloads have been added to io_context and
thread_pool that accept a service_maker. For example:

  class my_service_maker : public execution_context::service_maker
  {
  public:
    void make(execution_context& ctx) override
    {
      make_service<my_service>(ctx);
    }
  };

  io_context ctx{my_service_maker{}};
  • Loading branch information
chriskohlhoff committed Oct 29, 2024
1 parent 833e047 commit 387aa3b
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 3 deletions.
31 changes: 30 additions & 1 deletion asio/include/asio/execution_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,23 @@ class execution_context
public:
class id;
class service;
class service_maker;

public:
/// Constructor.
ASIO_DECL execution_context();

/// Constructor.
/**
* Construct with a service maker, to create an initial set of services that
* will be installed into the execution context at construction time.
*
* @param initial_services Used to create the initial services. The @c make
* function will be called once at the end of execution_context construction.
*/
ASIO_DECL explicit execution_context(
const service_maker& initial_services);

/// Destructor.
ASIO_DECL ~execution_context();

Expand Down Expand Up @@ -287,7 +299,7 @@ class execution_context::id
id() {}
};

/// Base class for all io_context services.
/// Base class for all execution context services.
class execution_context::service
: private noncopyable
{
Expand Down Expand Up @@ -330,6 +342,23 @@ class execution_context::service
service* next_;
};

/// Base class for all execution context service makers.
/**
* A service maker is called by the execution context to create services that
* need to be installed into the execution context at construction time.
*/
class execution_context::service_maker
: private noncopyable
{
public:
/// Make services to be added to the execution context.
virtual void make(execution_context& context) const = 0;

protected:
/// Destructor.
ASIO_DECL virtual ~service_maker();
};

/// Exception thrown when trying to add a duplicate service to an
/// execution_context.
class service_already_exists
Expand Down
11 changes: 11 additions & 0 deletions asio/include/asio/impl/execution_context.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ execution_context::execution_context()
{
}

execution_context::execution_context(
const execution_context::service_maker& initial_services)
: service_registry_(new asio::detail::service_registry(*this))
{
initial_services.make(*this);
}

execution_context::~execution_context()
{
shutdown();
Expand Down Expand Up @@ -65,6 +72,10 @@ void execution_context::service::notify_fork(execution_context::fork_event)
{
}

execution_context::service_maker::~service_maker()
{
}

service_already_exists::service_already_exists()
: std::logic_error("Service already exists.")
{
Expand Down
7 changes: 7 additions & 0 deletions asio/include/asio/impl/io_context.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ io_context::io_context(int concurrency_hint)
{
}

io_context::io_context(const execution_context::service_maker& initial_services)
: execution_context(initial_services),
impl_(add_impl(new impl_type(*this,
ASIO_CONCURRENCY_HINT_DEFAULT, false)))
{
}

io_context::impl_type& io_context::add_impl(io_context::impl_type* impl)
{
asio::detail::scoped_ptr<impl_type> scoped_impl(impl);
Expand Down
13 changes: 13 additions & 0 deletions asio/include/asio/impl/thread_pool.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@ thread_pool::thread_pool(std::size_t num_threads)
threads_.create_threads(f, static_cast<std::size_t>(num_threads_));
}

thread_pool::thread_pool(std::size_t num_threads,
const execution_context::service_maker& initial_services)
: execution_context(initial_services),
scheduler_(add_scheduler(new detail::scheduler(
*this, num_threads == 1 ? 1 : 0, false))),
num_threads_(detail::clamp_thread_pool_size(num_threads))
{
scheduler_.work_started();

thread_function f = { &scheduler_ };
threads_.create_threads(f, static_cast<std::size_t>(num_threads_));
}

thread_pool::~thread_pool()
{
stop();
Expand Down
11 changes: 11 additions & 0 deletions asio/include/asio/io_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ class io_context
*/
ASIO_DECL explicit io_context(int concurrency_hint);

/// Constructor.
/**
* Construct with a service maker, to create an initial set of services that
* will be installed into the execution context at construction time.
*
* @param initial_services Used to create the initial services. The @c make
* function will be called once at the end of execution_context construction.
*/
ASIO_DECL explicit io_context(
const execution_context::service_maker& initial_services);

/// Destructor.
/**
* On destruction, the io_context performs the following sequence of
Expand Down
13 changes: 13 additions & 0 deletions asio/include/asio/thread_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ class thread_pool
/// Constructs a pool with a specified number of threads.
ASIO_DECL thread_pool(std::size_t num_threads);

/// Constructs a pool with a specified number of threads.
/**
* Construct with a service maker, to create an initial set of services that
* will be installed into the execution context at construction time.
*
* @param num_threads The number of threads required.
*
* @param initial_services Used to create the initial services. The @c make
* function will be called once at the end of execution_context construction.
*/
ASIO_DECL thread_pool(std::size_t num_threads,
const execution_context::service_maker& initial_services);

/// Destructor.
/**
* Automatically stops and joins the pool, if not explicitly done beforehand.
Expand Down
6 changes: 6 additions & 0 deletions asio/src/doc/quickref.xml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
<member><link linkend="asio.reference.execution_context">execution_context</link></member>
<member><link linkend="asio.reference.execution_context__id">execution_context::id</link></member>
<member><link linkend="asio.reference.execution_context__service">execution_context::service</link></member>
<member><link linkend="asio.reference.execution_context__service_maker">execution_context::service_maker</link></member>
<member><link linkend="asio.reference.executor">executor</link></member>
<member><link linkend="asio.reference.executor_arg_t">executor_arg_t</link></member>
<member><link linkend="asio.reference.invalid_service_owner">invalid_service_owner</link></member>
Expand All @@ -163,6 +164,7 @@
<member><link linkend="asio.reference.io_context__service">io_context::service</link></member>
<member><link linkend="asio.reference.io_context__strand">io_context::strand</link></member>
<member><link linkend="asio.reference.multiple_exceptions">multiple_exceptions</link></member>
<member><link linkend="asio.reference.no_error_t">no_error_t</link></member>
<member><link linkend="asio.reference.partial_as_tuple">partial_as_tuple</link></member>
<member><link linkend="asio.reference.partial_redirect_error">partial_redirect_error</link></member>
<member><link linkend="asio.reference.service_already_exists">service_already_exists</link></member>
Expand Down Expand Up @@ -196,6 +198,7 @@
<member><link linkend="asio.reference.cancellation_slot_binder">cancellation_slot_binder</link></member>
<member><link linkend="asio.reference.consign_t">consign_t</link></member>
<member><link linkend="asio.reference.deferred_t">deferred_t</link></member>
<member><link linkend="asio.reference.disposition_traits">disposition_traits</link></member>
<member><link linkend="asio.reference.executor_binder">executor_binder</link></member>
<member><link linkend="asio.reference.executor_work_guard">executor_work_guard</link></member>
<member><link linkend="asio.reference.experimental__as_single_t">experimental::as_single_t</link></member>
Expand Down Expand Up @@ -267,6 +270,8 @@
<member><link linkend="asio.reference.spawn">spawn</link></member>
<member><link linkend="asio.reference.this_coro__reset_cancellation_state">this_coro::reset_cancellation_state</link></member>
<member><link linkend="asio.reference.this_coro__throw_if_cancelled">this_coro::throw_if_cancelled</link></member>
<member><link linkend="asio.reference.throw_exception">throw_exception</link></member>
<member><link linkend="asio.reference.to_exception_ptr">to_exception_ptr</link></member>
<member><link linkend="asio.reference.execution_context.use_service">use_service</link></member>
</simplelist>
<bridgehead renderas="sect3">Special Values</bridgehead>
Expand All @@ -277,6 +282,7 @@
<member><link linkend="asio.reference.executor_arg">executor_arg</link></member>
<member><link linkend="asio.reference.experimental__use_coro">experimental::use_coro</link></member>
<member><link linkend="asio.reference.experimental__use_promise">experimental::use_promise</link></member>
<member><link linkend="asio.reference.no_error">no_error</link></member>
<member><link linkend="asio.reference.this_coro__cancellation_state">this_coro::cancellation_state</link></member>
<member><link linkend="asio.reference.this_coro__executor">this_coro::executor</link></member>
<member><link linkend="asio.reference.use_awaitable">use_awaitable</link></member>
Expand Down
54 changes: 52 additions & 2 deletions asio/src/tests/unit/io_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,14 +268,56 @@ class test_service : public asio::io_context::service
{
public:
static asio::io_context::id id;

test_service(asio::io_context& s)
: asio::io_context::service(s) {}
: asio::io_context::service(s)
{
}

private:
virtual void shutdown() {}
void shutdown() override
{
}
};

asio::io_context::id test_service::id;

class test_context_service : public asio::execution_context::service
{
public:
static asio::execution_context::id id;

test_context_service(asio::execution_context& c, int value = 0)
: asio::execution_context::service(c),
value_(value)
{
}

int get_value() const
{
return value_;
}

private:
void shutdown() override
{
}

int value_;
};

asio::execution_context::id test_context_service::id;

class test_context_service_maker :
public asio::execution_context::service_maker
{
public:
void make(asio::execution_context& ctx) const override
{
(void)asio::make_service<test_context_service>(ctx, 42);
}
};

void io_context_service_test()
{
asio::io_context ioc1;
Expand Down Expand Up @@ -332,6 +374,14 @@ void io_context_service_test()
delete svc4;

ASIO_CHECK(!asio::has_service<test_service>(ioc3));

// Initial service registration.

asio::io_context ioc4{test_context_service_maker{}};

ASIO_CHECK(asio::has_service<test_context_service>(ioc4));
ASIO_CHECK(asio::use_service<test_context_service>(ioc4).get_value()
== 42);
}

void io_context_executor_query_test()
Expand Down
42 changes: 42 additions & 0 deletions asio/src/tests/unit/thread_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,40 @@ class test_service : public asio::execution_context::service
asio::execution_context::id test_service::id;
#endif // defined(ASIO_NO_TYPEID)

class test_context_service : public asio::execution_context::service
{
public:
static asio::execution_context::id id;

test_context_service(asio::execution_context& c, int value = 0)
: asio::execution_context::service(c),
value_(value)
{
}

int get_value() const
{
return value_;
}

private:
virtual void shutdown() {}

int value_;
};

asio::execution_context::id test_context_service::id;

class test_context_service_maker :
public asio::execution_context::service_maker
{
public:
void make(asio::execution_context& ctx) const override
{
(void)asio::make_service<test_context_service>(ctx, 42);
}
};

void thread_pool_service_test()
{
asio::thread_pool pool1(1);
Expand Down Expand Up @@ -154,6 +188,14 @@ void thread_pool_service_test()
delete svc4;

ASIO_CHECK(!asio::has_service<test_service>(pool3));

// Initial service registration.

asio::thread_pool pool4{1, test_context_service_maker{}};

ASIO_CHECK(asio::has_service<test_context_service>(pool4));
ASIO_CHECK(asio::use_service<test_context_service>(pool4).get_value()
== 42);
}

void thread_pool_executor_query_test()
Expand Down

0 comments on commit 387aa3b

Please sign in to comment.