Configuration
ThreadPoolConfig controls the pool settings that apply when a ThreadPool is created.
For most applications, the default configuration is enough:
#include <vix/threadpool/all.hpp>
int main()
{
vix::threadpool::ThreadPool pool;
auto future = pool.submit([](){
return 42;
});
return future.get() == 42 ? 0 : 1;
}Use an explicit configuration when the application needs control over worker count, queue capacity, shutdown behavior, or the default task timeout.
Create a configured pool
Create a ThreadPoolConfig, modify the required fields, then pass it to ThreadPool:
#include <vix/threadpool/all.hpp>
int main()
{
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
config.max_queue_size = 256;
config.drain_on_shutdown = true;
vix::threadpool::ThreadPool pool(config);
auto future = pool.submit([](){
return 42;
});
return future.get() == 42 ? 0 : 1;
}The configuration is normalized when the pool is constructed. The resulting configuration remains available through config().
const auto& config = pool.config();Configuration fields
ThreadPoolConfig currently exposes these fields:
| Field | Default | Current ThreadPool behavior |
|---|---|---|
thread_count | hardware concurrency, or 1 | Controls the number of workers created |
max_thread_count | same as the default worker count | Normalized and stored, but does not currently cause dynamic worker creation |
max_queue_size | 0 | Sets the maximum queued task count for each worker |
default_priority | TaskPriority::normal | Stored in the configuration, but not currently applied as the default submission priority |
allow_dynamic_growth | false | Stored, but the current pool uses a fixed worker set |
drain_on_shutdown | true | Controls whether queued work is drained during shutdown |
swallow_task_exceptions | true | Stored, but does not currently change ThreadPool task exception handling |
idle_wait | 0us | Stored, but does not currently configure the worker wait strategy |
default_timeout | 0ms | Applied to submitted tasks that do not provide their own timeout |
The fields that currently affect the ThreadPool runtime are therefore:
thread_count
max_queue_size
drain_on_shutdown
default_timeoutThe remaining fields are part of the public configuration structure but should not currently be relied on to change runtime behavior.
Worker count
thread_count controls how many workers are created.
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
vix::threadpool::ThreadPool pool(config);This creates four workers and four worker threads.
The same configuration can be expressed with the worker-count constructor:
vix::threadpool::ThreadPool pool(4);Default worker count
The default configuration uses:
vix::threadpool::ThreadPoolConfig::default_thread_count();This returns std::thread::hardware_concurrency() when it reports a non-zero value. If the platform cannot provide a hardware concurrency value, the result is 1.
The following therefore creates a pool using the default worker count:
vix::threadpool::ThreadPool pool;Zero worker count
A configured thread_count of zero requests the default worker count.
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 0;
vix::threadpool::ThreadPool pool(config);Normalization guarantees that the final worker count is never zero.
You can inspect the actual worker count through:
const std::size_t workers = pool.thread_count();Queue capacity
max_queue_size controls the maximum number of queued tasks for each worker.
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
config.max_queue_size = 128;
vix::threadpool::ThreadPool pool(config);The limit is applied independently to every worker queue.
With this configuration:
thread_count = 4
max_queue_size = 128the runtime is conceptually:
Worker 1 → queue capacity 128
Worker 2 → queue capacity 128
Worker 3 → queue capacity 128
Worker 4 → queue capacity 128max_queue_size is therefore not a global limit across the entire pool.
A value of zero means that worker queues are unbounded:
vix::threadpool::ThreadPoolConfig config;
config.max_queue_size = 0;When a bounded worker queue cannot accept additional work, submission can be rejected.
See Queue and Rejection Policies.
Shutdown draining
drain_on_shutdown controls what workers do with queued work when shutdown begins.
The default is:
config.drain_on_shutdown = true;With draining enabled, workers continue processing work already present in their queues before stopping.
shutdown
↓
stop accepting ordinary work
↓
finish queued work
↓
workers exit
↓
threads joinTo request a faster stop without draining all queued work:
vix::threadpool::ThreadPoolConfig config;
config.drain_on_shutdown = false;
vix::threadpool::ThreadPool pool(config);This setting affects work that has not started yet. A worker thread is not forcefully terminated while arbitrary C++ code is executing.
See Lifecycle and Shutdown for the complete lifecycle behavior.
Default timeout
default_timeout provides a timeout for tasks that do not explicitly specify one.
By default, it is disabled:
config.default_timeout = std::chrono::milliseconds{0};A positive duration enables the pool default:
#include <chrono>
#include <vix/threadpool/all.hpp>
int main()
{
vix::threadpool::ThreadPoolConfig config;
config.default_timeout = std::chrono::milliseconds{500};
vix::threadpool::ThreadPool pool(config);
auto future = pool.submit([](){
return 42;
});
return future.get() == 42 ? 0 : 1;
}If a task already has its own timeout, that task-specific timeout is kept instead of the pool default.
Conceptually:
task has timeout?
│
├── yes → use task timeout
│
└── no
↓
pool default timeout enabled?
│
├── yes → use pool default
│
└── no → timeout disabledTimeouts observe task execution duration. They do not forcibly terminate arbitrary C++ code.
See Timeouts for the exact timeout contract.
Configuration normalization
ThreadPoolConfig::normalized() returns a corrected copy of a configuration.
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
config.max_thread_count = 2;
const auto normalized = config.normalized();The normalization rules are:
thread_count == 0
↓
use default_thread_count()
thread_count still == 0
↓
use 1
max_thread_count == 0
↓
use thread_count
max_thread_count < thread_count
↓
raise it to thread_countThe pool constructor normalizes automatically:
vix::threadpool::ThreadPool pool(config);Applications normally do not need to call normalized() themselves.
Maximum thread count and dynamic growth
ThreadPoolConfig exposes:
config.max_thread_count;
config.allow_dynamic_growth;These fields are normalized and retained in pool.config().
For example:
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 2;
config.max_thread_count = 8;
config.allow_dynamic_growth = true;
vix::threadpool::ThreadPool pool(config);The current ThreadPool runtime still creates only the configured thread_count workers.
It does not currently grow from two workers to eight workers when the queue becomes busy.
Code should therefore use thread_count as the effective worker count for the current implementation.
Default priority
ThreadPoolConfig contains:
config.default_priority;Its default value is:
vix::threadpool::TaskPriority::normalThe current submission path does not merge this field into TaskOptions.
Default-constructed task options already use normal priority:
vix::threadpool::TaskOptions options;To select another priority, configure the task explicitly:
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_priority(
vix::threadpool::TaskPriority::high
);
auto future = pool.submit([](){
return 42;
}, options);Do not currently rely on config.default_priority to change the priority of ordinary submissions.
See Priorities.
Exception configuration
ThreadPoolConfig also exposes:
config.swallow_task_exceptions;The current ThreadPool implementation captures exceptions from submitted work independently of this field.
For result-producing submissions, exceptions are associated with the asynchronous result and can be observed through the corresponding Future.
For fire-and-forget work, exceptions do not escape the worker thread.
Changing swallow_task_exceptions currently does not change those behaviors.
See Futures and Promises and Errors.
Idle wait
The configuration contains:
config.idle_wait;but the current ThreadPool does not pass this value to its worker waiting mechanism.
Changing idle_wait should therefore not currently be used as a worker scheduling or performance control.
Worker waiting behavior is explained in Execution Model.
Read the effective configuration
The normalized configuration used to construct the pool can be inspected with:
const auto& config = pool.config();For example:
#include <iostream>
#include <vix/threadpool/all.hpp>
int main()
{
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
config.max_queue_size = 128;
vix::threadpool::ThreadPool pool(config);
std::cout << "workers: " << pool.config().thread_count << '\n';
std::cout << "queue limit: " << pool.config().max_queue_size << '\n';
return 0;
}config() returns the normalized configuration stored by the pool. It does not mean that every field in the structure currently drives runtime behavior.
Pool configuration vs task configuration
Pool configuration and task configuration solve different problems.
ThreadPoolConfig describes the runtime:
worker count
queue capacity
shutdown draining
default timeoutTaskOptions describes one submitted task:
priority
timeout
deadline
cancellation
worker affinityFor example:
vix::threadpool::ThreadPoolConfig config;
config.thread_count = 4;
config.max_queue_size = 256;
vix::threadpool::ThreadPool pool(config);
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_priority(
vix::threadpool::TaskPriority::high
);
auto future = pool.submit([](){
return 42;
}, options);The first configuration creates the execution environment. The second describes how one task should be handled inside that environment.
Continue with Executors to begin the execution model, or Tasks and Options for task-specific configuration.