Execution Contexts
This page explains execution contexts, service management, and the thread pool.
Code snippets assume using namespace boost::capy; is in effect.
|
What is an Execution Context?
An execution context is a place where work runs. It provides:
-
A registry of services (polymorphic components)
-
An associated executor type
-
Lifecycle management (shutdown, destroy)
The execution_context class is the base class for all contexts:
class io_context : public execution_context
{
public:
using executor_type = /* ... */;
executor_type get_executor();
~io_context()
{
shutdown();
destroy();
}
};
Service Management
Services are polymorphic components owned by an execution context. Each service type can be registered at most once.
Creating Services
// Get or create a service
my_service& svc = ctx.use_service<my_service>();
// Explicitly create with arguments
my_service& svc = ctx.make_service<my_service>(arg1, arg2);
// Check if a service exists
if (ctx.has_service<my_service>())
// ...
// Find without creating
my_service* svc = ctx.find_service<my_service>(); // nullptr if not found
Implementing Services
Services derive from execution_context::service:
struct my_service : execution_context::service
{
explicit my_service(execution_context& ctx)
{
// Initialize...
}
protected:
void shutdown() override
{
// Cancel pending operations
// Release resources
// Must not block or throw
}
};
The shutdown() method is called when the context is destroyed, in reverse
order of service creation.
Key Type Aliasing
Services can specify a key_type to enable base-class lookup:
struct file_service : execution_context::service
{
protected:
void shutdown() override {}
};
struct posix_file_service : file_service
{
using key_type = file_service; // Register under base class
explicit posix_file_service(execution_context& ctx) {}
};
// Usage:
ctx.make_service<posix_file_service>();
file_service* svc = ctx.find_service<file_service>(); // Returns posix_file_service*
Handler Queue
The execution_context::queue class stores completion handlers:
class queue
{
public:
bool empty() const noexcept;
void push(handler* h) noexcept;
void push(queue& other) noexcept; // Splice
handler* pop() noexcept;
};
Handlers implement the ownership contract:
-
Call
operator()for normal invocation (handler cleans itself up) -
Call
destroy()to discard without invoking (e.g., during shutdown) -
Never call both, and never use
deletedirectly
Thread Pool
The thread_pool class provides a pool of worker threads:
thread_pool pool(4); // 4 worker threads
auto ex = pool.get_executor();
async_run(ex)(my_task());
// ... work runs on pool threads ...
Lifecycle Pattern
Derived contexts must follow this destruction pattern:
class my_context : public execution_context
{
public:
~my_context()
{
shutdown(); // Notify services, cancel pending work
destroy(); // Delete services in reverse order
// Now safe to destroy members
}
};
Calling shutdown() and destroy() from the base class destructor is too
late—derived class members may already be destroyed.
The is_execution_context Concept
Types satisfying is_execution_context can be used with framework components:
template<class X>
concept is_execution_context =
std::derived_from<X, execution_context> &&
requires { typename X::executor_type; } &&
executor<typename X::executor_type> &&
requires(X& x) {
{ x.get_executor() } -> std::same_as<typename X::executor_type>;
};
Thread Safety
Service management functions (use_service, make_service, find_service)
are thread-safe. The shutdown() and destroy() functions are NOT thread-safe
and must only be called during destruction.
When NOT to Use execution_context Directly
Use execution_context directly when:
-
Building a custom I/O context
-
Implementing a new execution model
-
Managing polymorphic services
Do NOT use execution_context directly when:
-
You just need to run coroutines — use an existing context like Asio’s
-
You need a thread pool — use
thread_pooldirectly
Summary
| Component | Purpose |
|---|---|
|
Base class providing service registry |
|
Polymorphic component owned by a context |
|
Base class for completion callbacks |
|
FIFO queue of handlers |
|
Multi-threaded execution context |
|
Concept for valid execution contexts |
Next Steps
-
Frame Allocation — Optimize coroutine memory
-
is_execution_context — Reference