tmc::ex_manual_st#
An executor that does not own any threads. Work can be posted to this
executor at any time, but it will only be executed when you call one of
the run_*() functions. The caller of run_*() will execute work inline on
the current thread.
It is safe to post work from any number of threads concurrently, but
run_*() must only be called from 1 thread at a time.
This allows you to poll for continuations at a specific time, such as in the main loop of a game engine.
Usage Example#
This example uses ex_manual_st to poll for completions of both background work
(which may complete at any time) and specific work which must be completed before we can proceed.
tmc::ex_manual_st mainLoopExecutor;
tmc::task<void> loadEntity(Entity& entity) {
co_await readFile(...);
parseFile(...); // CPU-bound work to be done on the multi-threaded ex_cpu
co_await tmc::resume_on(mainLoopExecutor);
spawnEntity(...); // must be done on main thread for sync reasons
}
int main() {
tmc::cpu_executor().init();
mainLoopExecutor.init();
while (true) {
// Distant entities can be loaded at any time. We don't need to wait for
// them to finish.
std::vector<Entity> distant = get_new_distant_entities();
for (auto& e : distant) {
tmc::post(tmc::cpu_executor(), loadEntity(e));
}
// Nearby entities must be loaded this frame.
std::vector<Entity> nearby = get_new_nearby_entities();
std::future<void> nearbyReady = tmc::post_bulk_waitable(
tmc::cpu_executor(),
nearby | std::ranges::views::transform(loadEntity)
);
// Continue polling until all nearby entities are loaded.
// If any distant entities are ready, they will be processed too.
while (nearbyReady.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready) {
mainLoopExecutor.run_all();
}
render();
}
}
API Reference#
-
class ex_manual_st#
An executor that does not own any threads. Work can be posted to this executor at any time, but it will only be executed when you call one of the
run_*()functions. The caller ofrun_*()will execute work inline on the current thread.It is safe to post work from any number of threads concurrently, but
run_*()must only be called from 1 thread at a time.Public Functions
-
bool run_one()#
Attempt to run 1 work item from the executor’s queue. Work items will be executed on the current thread. Returns true if any work was waiting, and 1 work item was executed. Returns false if the executor’s queue was empty.
-
size_t run_all()#
Run all work items from the executor’s queue. Work items will be executed on the current thread. Returns the number of work items that were executed (0 if it was empty).
The returned count may be larger than the number of work items originally posted, because awaitables may resume suspended tasks by posting them back to the executor queue.
-
size_t run_n(const size_t MaxCount)#
Run up to MaxCount work items from the executor’s queue. Work items will be executed on the current thread. Returns the number of work items that were executed (0 if it was empty). MaxCount must be non-zero.
The returned count may be larger than the number of work items originally posted, because awaitables may resume suspended tasks by posting them back to the executor queue.
-
bool empty()#
Returns true if the executor’s queue appears to be empty at the current moment.
-
ex_manual_st &set_priority_count(size_t PriorityCount)#
Builder func to set the number of priority levels before calling
init(). The value must be in the range [1, 16]. The default is 1.
-
size_t priority_count()#
Gets the number of priority levels. Only useful after
init()has been called.
-
void init()#
Initializes the executor. If you want to customize the behavior, call the
set_X()functions before callinginit(). By default, uses hwloc to automatically generate threads, and creates 1 (or TMC_PRIORITY_COUNT) priority levels.If the executor is already initialized, calling
init()will do nothing.
-
void teardown()#
Stops the executor and destroys resources. Does not wait for any queued work to complete.
Restores the executor to an uninitialized state. After calling
teardown(), you may callset_X()to reconfigure the executor and callinit()again.If the executor is not initialized, calling
teardown()will do nothing.
-
~ex_manual_st()#
Invokes
teardown().
-
void post(work_item &&Item, size_t Priority = 0, size_t ThreadHint = NO_HINT)#
Submits a single work_item to the executor. If Priority is out of range, it will be clamped to an in-range value.
Rather than calling this directly, it is recommended to use the
tmc::post()free function template.
-
tmc::ex_any *type_erased()#
Returns a pointer to the type erased
ex_anyversion of this executor. This object shares a lifetime with this executor, and can be used for pointer-based equality comparison against the thread-localtmc::current_executor().
-
template<typename It>
inline void post_bulk(It &&Items, size_t Count, size_t Priority = 0, size_t ThreadHint = NO_HINT)# Submits
countitems to the executor.Itis expected to be an iterator type that implementsoperator*()andIt& operator++(). If Priority is out of range, it will be clamped to an in-range value.Rather than calling this directly, it is recommended to use the
tmc::post_bulk()free function template.
-
bool run_one()#