Dispatch Queue / Thread Pool implementation for C++11 with built-in C++20 coroutine support.
Features
- No external dependencies: uses only the C++ STL
- Supports both immediate and threaded execution modes:
- Threaded dispatch queues are also known as Thread Pools. In threaded mode it is safe to dispatch new tasks from any thread.
- In immediate mode tasks are executed immediately. Useful for multiplatform code that must work on platforms without thread support, for example WebAssembly on browsers that lack
SharedArrayBuffer support.
- Use
dispatch_queue.dispatch(f, args...) to dispatch new tasks
- Use
dispatch_queue.dispatch_main(f, args...) to dispatch "main loop" tasks
- Users must call
dispatch_queue.main_loop() manually where appropriate to run queued main loop tasks
- Useful for synchronizing state calculated in background tasks with the application's main loop
- Returned
dispatch_queue::task<T> from dispatch methods are similar to std::shared_future, with the following additions:
- Use
task.get_state() to get whether task is pending, ready or failed with exception
- Use
task.then(f) to add a continuation function that runs when task finishes
- Use
task.get_exception() to get stored exception_ptr
- Built-in C++20 coroutine support
- Use
dispatch_queue::task<T> as the return value for your coroutines
co_await other tasks to resume the coroutine as the task's continuation
- Use
co_await dispatch_queue.dispatch() to continue coroutine in a dispatch queue's background loop
- Use
co_await dispatch_queue.dispatch_main() to continue coroutine in a dispatch queue's main loop
- Supports compiling with
-fno-exceptions and -fno-rtti
- Unified implementation file src/dispatch_queue-one.cpp, easy to integrate in any project
Usage example
#include <dispatch_queue.hpp>
auto work = []{ return 42; };
assert(task.
get() == 42);
auto work2 = [](int value) { return value; };
assert(task2.
get() == 2);
std::rethrow_exception(exception);
}
else {
return (float) result;
}
})
return dispatcher.dispatch(work2);
})
return;
});
dispatcher.dispatch_main([]{
std::cout << "This will run inside the call to `main_loop`" << std::endl;
});
while (!ApplicationShouldExit()) {
dispatcher.main_loop();
}
co_await dispatcher.dispatch(some_work);
do_something_after_some_work_finished();
co_await dispatcher.dispatch();
do_something_in_background();
co_await dispatcher.dispatch_main();
do_something_in_main_loop();
}
int dispatcher_thread_count = dispatcher.thread_count();
bool dispatcher_is_threaded = dispatcher.is_threaded();
int pending_task_count = dispatcher.size();
bool has_no_pending_tasks = dispatcher.empty();
dispatcher.clear();
dispatcher.wait();
dispatcher.wait_for(std::chrono::seconds(5));
dispatcher.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(5));
Definition dispatch_queue.hpp:13
T get() const
Definition task.hpp:71
void wait() const
Definition task.hpp:95
std::exception_ptr get_exception() const
Definition task.hpp:85
auto then(F &&f) const
Definition task.hpp:59
Using in CMake projects
Add this project using add_subdirectory and link your target to dispatch_queue:
add_subdirectory("path/to/dispatch_queue")
target_link_libraries(my_target dispatch_queue)
Setting thread names for debugging
You can pass a functor to the dispatch queue constructor that will run inside worker threads when they initialize. There you can set thread names:
std::string worker_name = std::format("worker{}", worker_index);
});