A single-header, portable C++17 thread pool library.
I needed a small, portable thread pool that also gracefully simplified to single-threaded execution when used with 0 threads or compiled with emscripten without pthread support. Also, SmallThreadPool is small -- about 1000 lines of heavily doxygen-commented code.
The overall design is based on the article and code by Max Liani, with some ideas (for the higher-level template wrappers) taken from Wenzel Jakob's nanothread library.
Just
#include "smallthreadpool.h"to get started.
In one translation unit (source file), you must #define SMALL_THREADPOOL_IMPLEMENTATION before including the header.
One of the core constructs is the parallel for-loop. Here's an example of how to use it:
#include <spdlog/spdlog.h>
#define SMALL_THREADPOOL_IMPLEMENTATION
#include <smallthreadpool.h>
int main(int, char **) {
int result[100];
// Call the provided lambda function 100 times with blocks of size 1
stp::parallel_for(
blocked_range<uint32_t>(0 // begin,
100 // end,
1 // block_size
),
// The callback is allowed to be a stateful lambda function
[&](uint32_t begin, uint32_t end, int unit_index, int thread_index) {
for (uint32_t i = begin; i != end; ++i) {
spdlog::info("Worker thread {} is starting to process work unit {}\n",
pool_thread_id(), i);
// Write to variables defined in the caller's frame
result[i] = i;
}
}
);
}There are a number of other constructs provided by smallthreadpool, including a do_async function to perform a single computation asynchronously on the thread pool, a Barrier thread-coordination mechanism class, and a for_each_thread function to execute a function once for each thread in the thread pool. See the extensive source code doxygen comments for more usage info and examples.
The library itself needs no build system. The test suite does, so its CMake project lives under tests/ and is configured directly:
cmake -S tests -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failureUseful options:
| Option | Values | Meaning |
|---|---|---|
STP_TESTS_CXX_STANDARD |
17 (default), 20 |
C++ standard to build the tests with |
STP_TESTS_SANITIZER |
empty, Address;Undefined, Thread |
Build under the given sanitizer |
STP_TESTS_WERROR |
ON (default), OFF |
Treat compiler warnings as errors |
For example, to hunt for data races:
cmake -S tests -B build-tsan -DCMAKE_BUILD_TYPE=Release -DSTP_TESTS_SANITIZER=Thread
cmake --build build-tsan
TSAN_OPTIONS=halt_on_error=1 ctest --test-dir build-tsan --output-on-failureThe suite deliberately compiles the implementation into its own translation unit, separate from the tests that use the header, since some defects only appear when the pool's statics are defined in one translation unit and read from another. CI runs the whole matrix (GCC, Clang, AppleClang and MSVC; C++17 and C++20; ASan+UBSan and TSan) on every push.