Tasks and Options
A task is one unit of work submitted to the ThreadPool runtime.
In normal application code, you usually provide a callable and let ThreadPool create the task:
auto future = pool.submit([](){
return 42;
});The module associates that callable with task identity, execution options, queue ordering, lifecycle state, and execution result.
TaskOptions lets the caller describe how that work should be handled.
Callables become tasks
The most common way to create work is through:
post()
submit()
handle()For example:
pool.post([](){
do_background_work();
});or:
auto future = pool.submit([](){
return compute_result();
});The application supplies the work. ThreadPool creates the task representation required by the scheduler and workers.
Conceptually:
callable
+
TaskOptions
↓
Task
↓
ThreadPool runtimeMost applications do not need to construct Task directly.
TaskOptions
TaskOptions describes properties of one submission.
Default options can be created with:
vix::threadpool::TaskOptions options;The defaults are:
priority normal
timeout disabled
deadline disabled
cancellation disconnected
affinity none
allow_after_stop false
detached false
flags 0The primary execution controls are:
priority
timeout
deadline
cancellation
worker affinityThere are also advanced fields for submission lifecycle and higher-level integrations.
Default submission
No options are required for ordinary work:
auto future = pool.submit([](){
return 42;
});This is equivalent to providing default task options:
vix::threadpool::TaskOptions options;
auto future = pool.submit([](){
return 42;
}, options);Use explicit options only when the task needs behavior different from the normal submission path.
Priority
Priority influences the ordering of queued work inside a worker queue.
The available values are:
lowest
low
normal
high
highestThe default is:
vix::threadpool::TaskPriority::normalUse with_priority() for a single setting:
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_priority(
vix::threadpool::TaskPriority::high
);
auto future = pool.submit([](){
return 42;
}, options);Or modify an existing options object:
vix::threadpool::TaskOptions options;
options.set_priority(vix::threadpool::TaskPriority::high);Priority does not create a global ordering across every worker in the pool.
See Priorities.
Timeout
A timeout observes how long task execution takes.
Create timeout options directly:
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_timeout(
vix::threadpool::Timeout::milliseconds(500)
);or modify existing options:
vix::threadpool::TaskOptions options;
options.set_timeout(
vix::threadpool::Timeout::milliseconds(500)
);Check whether timeout observation is enabled with:
if (options.has_timeout())
{
// A timeout is configured.
}A timeout does not forcibly terminate arbitrary C++ code.
See Timeouts.
Deadline
A deadline represents an absolute execution limit.
For example:
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_deadline(
vix::threadpool::Deadline::after(std::chrono::seconds{2})
);The setter form is:
vix::threadpool::TaskOptions options;
options.set_deadline(
vix::threadpool::Deadline::after(std::chrono::seconds{2})
);Check whether a deadline exists with:
if (options.has_deadline())
{
// A deadline is configured.
}If the deadline has already expired before execution begins, the task can be skipped.
See Deadlines.
Cancellation
A task can observe cancellation through a CancellationToken.
vix::threadpool::CancellationSource source;
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_cancellation(
source.token()
);
auto future = pool.submit([](){
return 42;
}, options);The setter form is:
vix::threadpool::TaskOptions options;
options.set_cancellation(source.token());Check whether the options contain a connected cancellation token:
if (options.has_cancellation())
{
// Cancellation can be requested.
}Cancellation is cooperative. It does not provide unsafe thread termination.
See Cancellation.
Worker affinity
Worker affinity expresses a preference for a particular worker.
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_affinity(
vix::threadpool::WorkerId{2}
);
auto future = pool.submit([](){
return 42;
}, options);The setter form is:
vix::threadpool::TaskOptions options;
options.set_affinity(vix::threadpool::WorkerId{2});Check whether affinity is configured with:
if (options.has_affinity())
{
// A worker affinity hint exists.
}The default affinity value is invalid_worker_id, which means no worker preference.
See Worker Affinity.
Combine multiple options
TaskOptions setters return the same options object, so settings can be chained.
vix::threadpool::CancellationSource source;
vix::threadpool::TaskOptions options;
options
.set_priority(vix::threadpool::TaskPriority::high)
.set_timeout(vix::threadpool::Timeout::milliseconds(500))
.set_cancellation(source.token())
.set_affinity(vix::threadpool::WorkerId{1});
auto future = pool.submit([](){
return 42;
}, options);The options describe one logical submission.
They do not modify the configuration of the entire pool.
Pool configuration and task options
ThreadPoolConfig and TaskOptions operate at different levels.
Pool configuration describes the execution environment:
ThreadPoolConfig
├── worker count
├── worker queue capacity
├── shutdown draining
└── default timeoutTask options describe one task:
TaskOptions
├── priority
├── timeout
├── deadline
├── cancellation
├── affinity
└── submission flagsFor 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 object configures the pool. The second configures one submission.
Default timeout merging
The pool can provide a default task timeout through ThreadPoolConfig.
vix::threadpool::ThreadPoolConfig config;
config.default_timeout = std::chrono::milliseconds{500};
vix::threadpool::ThreadPool pool(config);When a task has no explicit timeout, the pool applies its configured default.
TaskOptions timeout disabled
+
pool default_timeout enabled
↓
pool default is appliedAn explicit task timeout takes precedence:
vix::threadpool::TaskOptions options = vix::threadpool::TaskOptions::with_timeout(
vix::threadpool::Timeout::milliseconds(100)
);
auto future = pool.submit([](){
return 42;
}, options);In this case, the task keeps its 100 ms timeout instead of receiving the pool default.
Skip before execution
TaskOptions exposes:
options.should_skip_before_run();It returns true when either:
cancellation has already been requested
or
deadline has already expiredConceptually:
TaskOptions
↓
cancelled?
┌─┴─┐
yes no
│ │
skip ▼
deadline expired?
┌─┴─┐
yes no
│ │
skip continueThis check does not include timeout because timeout observation depends on execution duration.
allow_after_stop
Normal submissions are rejected once the pool stops accepting ordinary work.
TaskOptions contains an advanced exception:
vix::threadpool::TaskOptions options;
options.set_allow_after_stop(true);This allows submission during the limited shutdown interval where the ThreadPool has stopped accepting ordinary work but its scheduler is still running.
It is not a way to restart a stopped pool, and it does not allow submission after the scheduler has completely stopped.
Most application tasks should keep the default:
allow_after_stop = falseDetached tasks
TaskOptions contains a detached flag.
vix::threadpool::TaskOptions options;
options.set_detached(true);Normal application code usually does not need to set it manually.
ThreadPool::post() marks posted work as detached automatically:
pool.post([](){
do_background_work();
});The current runtime stores this property with the task options, but it does not otherwise select a different execution path based on the flag.
Use post() when fire-and-forget behavior is intended instead of manually setting detached on another submission API.
User-defined flags
TaskOptions also exposes:
std::uint32_t flags;The default value is:
0These bits are reserved for higher-level integrations.
The current ThreadPool runtime does not interpret them when scheduling or executing tasks.
Code that uses flags must therefore define its own higher-level meaning rather than expecting built-in ThreadPool behavior.
Static TaskOptions helpers
The module provides convenience constructors for the main task controls:
vix::threadpool::TaskOptions::with_priority(...)
vix::threadpool::TaskOptions::with_timeout(...)
vix::threadpool::TaskOptions::with_deadline(...)
vix::threadpool::TaskOptions::with_cancellation(...)
vix::threadpool::TaskOptions::with_affinity(...)Each helper starts from default options and changes one property.
For example:
auto options = vix::threadpool::TaskOptions::with_priority(
vix::threadpool::TaskPriority::highest
);is conceptually equivalent to:
vix::threadpool::TaskOptions options;
options.priority = vix::threadpool::TaskPriority::highest;The helper form makes the primary intent visible at the point of construction.
Task
Task is the low-level public representation of executable work.
It contains:
Task
├── TaskId
├── callable
├── TaskOptions
├── TaskStatus
├── TaskResult
├── sequence number
├── captured exception
├── creation time
├── start time
└── finish timeOrdinary users should normally submit callables through ThreadPool instead of manually constructing Task.
Direct Task construction is useful for lower-level integrations and code working directly with workers or queues.
Construct a Task directly
A task can be created from an ID, callable, options, and sequence number:
vix::threadpool::TaskOptions options;
vix::threadpool::Task task(
vix::threadpool::TaskId{1},
vix::threadpool::TaskFunction([](){
// Work.
}),
options,
1
);The initial state is:
status created
result noneThe task is not automatically submitted anywhere simply because a Task object exists.
The ThreadPool runtime normally handles task construction and queue insertion for the application.
Empty Task
A default-constructed task is invalid:
vix::threadpool::Task task;Its identifier is:
vix::threadpool::invalid_task_idand it does not contain executable work.
Check validity with:
if (task.valid())
{
// Task has a valid ID and callable.
}An invalid task is not schedulable.
TaskFunction is move-only
The callable stored by Task uses:
vix::threadpool::TaskFunctionThis function wrapper is move-only.
As a result, tasks can own non-copyable state.
For normal submissions:
auto value = std::make_unique<int>(42);
auto future = pool.submit([value = std::move(value)](){
return *value;
});The task can own the std::unique_ptr directly.
This avoids requiring submitted callables to be copyable.
Task is move-only
Task itself is also move-only.
copy construction disabled
copy assignment disabled
move construction supported
move assignment supportedThis matches its role as the owner of a move-only callable and task execution state.
Queues and scheduler components transfer tasks by moving them rather than copying them.
Task identity
Each task has a TaskId.
const auto id = task.id();TaskId is:
std::uint64_tand:
vix::threadpool::invalid_task_idis defined as zero.
Check an identifier with:
if (vix::threadpool::is_valid_task_id(id))
{
// Valid task ID.
}Normal pool submissions receive IDs from ThreadPool.
Applications usually encounter them through TaskHandle.
See Task Handles.
Task sequence
A task also carries a sequence number:
const std::uint64_t sequence = task.sequence();The sequence is different from the task ID.
TaskId
↓
identity
sequence
↓
stable queue orderingThe pool assigns monotonically increasing sequence numbers to submissions.
When tasks have equal priority inside the same worker queue, this sequence preserves FIFO ordering.
Task status
The current lifecycle state is available through:
const auto status = task.status();The lifecycle includes:
created
queued
running
completed
failed
cancelled
timed_out
rejectedConvenience checks include:
task.done();
task.running();
task.queued();The detailed status model is covered in Task Results and Status.
Task result
The execution result is available through:
const auto result = task.result();Possible values are:
none
success
failure
cancelled
timeout
rejectedConvenience checks include:
task.succeeded();
task.failed();Status describes where the task is in its lifecycle. Result describes how an execution attempt ended.
Task timing
Task records three time points:
task.created_at();
task.started_at();
task.finished_at();It also exposes observed execution duration:
const auto duration = task.execution_duration();Before execution starts, the duration is zero.
While the task is running, duration is measured from its start time to the current time.
After the task finishes, duration is measured between its recorded start and finish times.
These values use std::chrono::steady_clock, which is suitable for measuring elapsed execution time.
Captured exception
If low-level task execution throws, Task::run() catches the exception and stores it as an std::exception_ptr.
const std::exception_ptr error = task.exception();The exception does not escape the worker thread through the low-level task execution path.
For normal result-producing submissions, applications should use the returned Future instead of accessing a low-level Task exception directly.
See Futures and Promises.
Low-level execution
Task::run() executes the stored callable and returns a TaskResult.
const auto result = task.run();Before execution it checks:
task validity
cancellation
deadlineAfter the callable finishes it observes:
timeout
deadline
cancellationand records the final status and result.
Direct calls to Task::run() are primarily useful for lower-level integration and testing. Normal application code should allow pool workers to run submitted tasks.
Tasks should remain small units of scheduling
A task is the unit that the ThreadPool scheduler can place into a worker queue.
Once the callable starts, the worker executes that callable as ordinary C++ code until it returns or throws.
A single task is therefore not automatically parallelized internally.
For example:
auto future = pool.submit([](){
perform_large_operation();
});creates one scheduled task, regardless of how much work perform_large_operation() performs.
When a problem can be divided into independent pieces, submit multiple tasks or use one of the higher-level Parallel Algorithms.
Summary
The task model can be reduced to:
callable
+
TaskOptions
↓
Task
├── identity
├── ordering
├── lifecycle
└── result
↓
scheduler
↓
workerFor normal application code:
provide a callable
↓
optionally configure TaskOptions
↓
post(), submit(), or handle()
↓
let ThreadPool manage TaskUse direct Task construction only when lower-level control is genuinely required.
Continue with Task Handles for task identity and cancellation control, or Futures and Promises for result-producing work.