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 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();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_timeout
The remaining fields are part of the public configuration structure but should not currently be relied on to change runtime behavior.
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);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;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();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 = 128
the runtime is conceptually:
Worker 1 → queue capacity 128
Worker 2 → queue capacity 128
Worker 3 → queue capacity 128
Worker 4 → queue capacity 128
max_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.
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 join
To 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 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 disabled
Timeouts observe task execution duration. They do not forcibly terminate arbitrary C++ code.
See Timeouts for the exact timeout contract.
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_count
The pool constructor normalizes automatically:
vix::threadpool::ThreadPool pool(config);Applications normally do not need to call normalized() themselves.
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.
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.
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.
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.
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 and task configuration solve different problems.
ThreadPoolConfig describes the runtime:
worker count
queue capacity
shutdown draining
default timeout
TaskOptions describes one submitted task:
priority
timeout
deadline
cancellation
worker affinity
For 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.