Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
BS::thread_pool< OptFlags > Class Template Reference

A fast, lightweight, modern, and easy-to-use C++17/C++20/C++23 thread pool class. More...

#include <BS_thread_pool.hpp>

Collaboration diagram for BS::thread_pool< OptFlags >:

Public Member Functions

 thread_pool ()
 Construct a new thread pool. The number of threads will be the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. If the native extensions are enabled, the pool will instead use the number of threads available to the process, as obtained from BS::get_os_process_affinity(), which can be less than the number of hardware threads.
 
 thread_pool (const std::size_t num_threads)
 Construct a new thread pool with the specified number of threads.
 
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
 thread_pool (F &&init)
 Construct a new thread pool with the specified initialization function and the default number of threads.
 
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
 thread_pool (const std::size_t num_threads, F &&init)
 Construct a new thread pool with the specified number of threads and initialization function.
 
 thread_pool (const thread_pool &)=delete
 
 thread_pool (thread_pool &&)=delete
 
thread_pooloperator= (const thread_pool &)=delete
 
thread_pooloperator= (thread_pool &&)=delete
 
 ~thread_pool () noexcept
 Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. If a cleanup function was set, it will run in each thread right before it is destroyed. Note that if the pool is paused, then any tasks still in the queue will never be executed.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void detach_blocks (const T1 first_index, const T2 index_after_last, F &&block, const std::size_t num_blocks=0, const priority_t priority=0)
 Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The block function takes two arguments, the start and end of the block, so that it is only called once per block, but it is up to the user to make sure the block function correctly deals with all the indices in each block. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.
 
template<typename I >
void detach_bulk (const I first, const I last, const priority_t priority=0)
 Submit an iterator range containing functions with no arguments and no return values into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.
 
template<typename C >
void detach_bulk (C &container, const priority_t priority=0)
 Submit a container of functions with no arguments and no return values into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void detach_loop (const T1 first_index, const T2 index_after_last, F &&loop, const std::size_t num_blocks=0, const priority_t priority=0)
 Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The loop function takes one argument, the loop index, and it is called exactly once per index, but many times per block. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void detach_sequence (const T1 first_index, const T2 index_after_last, F &&sequence, const priority_t priority=0)
 Submit a sequence of tasks enumerated by indices to the queue, with the specified priority. The sequence function takes one argument, the task index, and will be called once per index. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the sequence finishes executing, otherwise bad things will happen.
 
template<typename F >
void detach_task (F &&task, const priority_t priority=0)
 Submit a function with no arguments and no return value into the task queue, with the specified priority. To submit a function with arguments, enclose it in a lambda expression. Does not return a future, so the user must use wait() or some other method to ensure that the task finishes executing, otherwise bad things will happen.
 
std::size_t get_tasks_queued () const
 Get the number of tasks currently waiting in the queue to be executed by the threads.
 
std::size_t get_tasks_running () const
 Get the number of tasks currently being executed by the threads.
 
std::size_t get_tasks_total () const
 Get the total number of unfinished tasks: either still waiting in the queue, or running in a thread. Note that get_tasks_total() == get_tasks_queued() + get_tasks_running().
 
std::size_t get_thread_count () const noexcept
 Get the number of threads in the pool.
 
std::vector< thread_t::id > get_thread_ids () const
 Get a vector containing the unique identifiers for each of the pool's threads, as obtained by std::thread::get_id() (or std::jthread::get_id() in C++20 and later).
 
BS_THREAD_POOL_IF_PAUSE_ENABLED bool is_paused () const
 Check whether the pool is currently paused. Only enabled if the flag BS::tp::pause is enabled in the template parameter.
 
BS_THREAD_POOL_IF_PAUSE_ENABLED void pause ()
 Pause the pool. The workers will temporarily stop retrieving new tasks out of the queue, although any tasks already executing will keep running until they are finished. Only enabled if the flag BS::tp::pause is enabled in the template parameter.
 
void purge ()
 Purge all the tasks waiting in the queue. Tasks that are currently running will not be affected, but any tasks still waiting in the queue will be discarded, and will never be executed by the threads. Please note that there is no way to restore the purged tasks.
 
void reset ()
 Reset the pool with the default number of threads (as if constructed with the default constructor). Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.
 
void reset (const std::size_t num_threads)
 Reset the pool with a new number of threads. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.
 
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void reset (F &&init)
 Reset the pool with the default number of threads and a new initialization function. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.
 
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void reset (const std::size_t num_threads, F &&init)
 Reset the pool with a new number of threads and a new initialization function. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.
 
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void set_cleanup_func (F &&cleanup)
 Set the thread pool's cleanup function.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F , typename R = std::invoke_result_t<std::decay_t<F>, T, T>>
multi_future< R > submit_blocks (const T1 first_index, const T2 index_after_last, F &&block, const std::size_t num_blocks=0, const priority_t priority=0)
 Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The block function takes two arguments, the start and end of the block, so that it is only called once per block, but it is up to the user to make sure the block function correctly deals with all the indices in each block. If the block function has a return value, get a BS::multi_future for the eventual returned values. If the block function has no return value, get a BS::multi_future<void> which can be used to wait until all the tasks finish.
 
template<typename I , typename F = decltype(*std::declval<I>()), typename R = std::invoke_result_t<std::decay_t<F>>>
multi_future< R > submit_bulk (const I first, const I last, const priority_t priority=0)
 Submit an iterator range containing functions with no arguments into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. If the functions have return values, get a BS::multi_future for the eventual returned values. If the functions have no return values, get a BS::multi_future<void> which can be used to wait until all the tasks finish.
 
template<typename C , typename F = decltype(*std::declval<C&>().begin()), typename R = std::invoke_result_t<std::decay_t<F>>>
multi_future< R > submit_bulk (C &container, const priority_t priority=0)
 Submit a container of functions with no arguments into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. If the functions have return values, get a BS::multi_future for the eventual returned values. If the functions have no return values, get a BS::multi_future<void> which can be used to wait until all the tasks finish.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
multi_future< void > submit_loop (const T1 first_index, const T2 index_after_last, F &&loop, const std::size_t num_blocks=0, const priority_t priority=0)
 Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The loop function takes one argument, the loop index, and it is called exactly once per index, but many times per block. Returns a BS::multi_future which can be used to wait until all the tasks finish.
 
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F , typename R = std::invoke_result_t<std::decay_t<F>, T>>
multi_future< R > submit_sequence (const T1 first_index, const T2 index_after_last, F &&sequence, const priority_t priority=0)
 Submit a sequence of tasks enumerated by indices to the queue, with the specified priority. The sequence function takes one argument, the task index, and will be called once per index. If the sequence function has a return value, get a BS::multi_future for the eventual returned values. If the sequence function has no return value, get a BS::multi_future<void> which can be used to wait until all the tasks finish.
 
template<typename F , typename R = std::invoke_result_t<std::decay_t<F>>>
std::future< R > submit_task (F &&task, const priority_t priority=0)
 Submit a function with no arguments into the task queue, with the specified priority. To submit a function with arguments, enclose it in a lambda expression. If the function has a return value, get a future for the eventual returned value. If the function has no return value, get an std::future<void> which can be used to wait until the task finishes.
 
BS_THREAD_POOL_IF_PAUSE_ENABLED void unpause ()
 Unpause the pool. The workers will resume retrieving new tasks out of the queue. Only enabled if the flag BS::tp::pause is enabled in the template parameter.
 
void wait ()
 Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the pool is paused, this function only waits for the currently running tasks (otherwise it would wait forever). Note: To wait for just one specific task, use submit_task() instead, and call the wait() member function of the generated future.
 
template<typename R , typename P >
bool wait_for (const std::chrono::duration< R, P > &duration)
 Wait for tasks to be completed, but stop waiting after the specified duration has passed.
 
template<typename C , typename D >
bool wait_until (const std::chrono::time_point< C, D > &timeout_time)
 Wait for tasks to be completed, but stop waiting after the specified time point has been reached.
 

Static Public Attributes

static constexpr bool priority_enabled = (OptFlags & tp::priority) != tp::none
 A flag indicating whether task priority is enabled.
 
static constexpr bool pause_enabled = (OptFlags & tp::pause) != tp::none
 A flag indicating whether pausing is enabled.
 
static constexpr bool wait_deadlock_checks_enabled = (OptFlags & tp::wait_deadlock_checks) != tp::none
 A flag indicating whether wait deadlock checks are enabled.
 

Private Member Functions

template<typename F >
void create_threads (const std::size_t num_threads, F &&init)
 Create the threads in the pool and assign a worker to each thread.
 
void destroy_threads ()
 Destroy the threads in the pool.
 
template<typename T , typename F , typename R , bool submit, typename N = std::conditional_t<submit, multi_future<R>, void>>
enqueue_blocks (const T first_index, const T index_after_last, F &&block, std::size_t num_blocks, const priority_t priority=0)
 A helper function for detach_blocks() and submit_blocks().
 
template<typename T , typename F , bool submit, typename N = std::conditional_t<submit, multi_future<void>, void>>
enqueue_loop (const T first_index, const T index_after_last, F &&loop, std::size_t num_blocks, const priority_t priority=0)
 A helper function for detach_loop() and submit_loop().
 
template<typename T , typename F , typename R , bool submit, typename N = std::conditional_t<submit, multi_future<R>, void>>
enqueue_sequence (const T first_index, const T index_after_last, F &&sequence, const priority_t priority=0)
 A helper function for detach_sequence() and submit_sequence().
 
task_t pop_task ()
 Pop a task from the queue.
 
template<typename F >
void reset_pool (const std::size_t num_threads, F &&init)
 Reset the pool with a new number of threads and a new initialization function. This member function implements the actual reset, while the public member function reset() also handles the case where the pool is paused.
 
void worker (BS_THREAD_POOL_WORKER_TOKEN const std::size_t idx)
 A worker function to be assigned to each thread in the pool. Waits until it is notified by detach_task() that a task is available, and then retrieves the task from the queue and executes it. Once the task finishes, the worker notifies wait() in case it is waiting.
 

Static Private Member Functions

static std::size_t determine_thread_count (const std::size_t num_threads) noexcept(!thread_pool_native_extensions)
 Determine how many threads the pool should have, based on the parameter passed to the constructor or reset().
 

Private Attributes

std::mutex tasks_mutex
 A mutex to synchronize access to the task queue by different threads.
 
std::condition_variable task_available_cv
 A condition variable to notify worker() that a new task has become available.
 
std::condition_variable tasks_done_cv
 A condition variable to notify wait() that the tasks are done.
 
move_only_function< void(std::size_t)> cleanup_func = [](std::size_t) {}
 A cleanup function to run in each thread right before it is destroyed, which will happen when the pool is destructed or reset. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. The cleanup function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function. The default is an empty function, i.e., no cleanup will be performed.
 
move_only_function< void(std::size_t)> init_func = [](std::size_t) {}
 An initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function. The default is an empty function, i.e., no initialization will be performed.
 
std::conditional_t< priority_enabled, std::priority_queue< pr_task >, std::queue< task_t > > tasks
 A queue of tasks to be executed by the threads.
 
std::size_t tasks_running = 0
 A counter for the total number of currently running tasks.
 
std::size_t thread_count = 0
 The number of threads in the pool.
 
std::unique_ptr< thread_t[]> threads = nullptr
 A smart pointer to manage the memory allocated for the threads.
 
std::conditional_t< pause_enabled, bool, std::monostate > paused = {}
 A flag indicating whether the workers should pause. When set to true, the workers temporarily stop retrieving new tasks out of the queue, although any tasks already executing will keep running until they are finished. When set to false again, the workers resume retrieving tasks. Only enabled if the flag BS::tp::pause is enabled in the template parameter.
 
bool waiting = false
 A flag indicating that wait() is active and expects to be notified whenever a task is done.
 
bool workers_running = false
 A flag indicating to the workers to keep running. When set to false, the workers terminate permanently.
 

Detailed Description

template<tp OptFlags = tp::none>
class BS::thread_pool< OptFlags >

A fast, lightweight, modern, and easy-to-use C++17/C++20/C++23 thread pool class.

Template Parameters
OptFlagsA bitmask of flags which can be used to enable optional features. The flags are members of the BS::tp enumeration: BS::tp::priority, BS::tp::pause, and BS::tp::wait_deadlock_checks. The default is BS::tp::none, which disables all optional features. To enable multiple features, use the bitwise OR operator |, e.g. BS::tp::priority | BS::tp::pause.

Constructor & Destructor Documentation

◆ thread_pool() [1/4]

template<tp OptFlags = tp::none>
BS::thread_pool< OptFlags >::thread_pool ( )
inline

Construct a new thread pool. The number of threads will be the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. If the native extensions are enabled, the pool will instead use the number of threads available to the process, as obtained from BS::get_os_process_affinity(), which can be less than the number of hardware threads.

1457: thread_pool(0, [] {}) {}
thread_pool()
Construct a new thread pool. The number of threads will be the total number of hardware threads avail...
Definition BS_thread_pool.hpp:1457

◆ thread_pool() [2/4]

template<tp OptFlags = tp::none>
BS::thread_pool< OptFlags >::thread_pool ( const std::size_t  num_threads)
inlineexplicit

Construct a new thread pool with the specified number of threads.

Parameters
num_threadsThe number of threads to use.
1464: thread_pool(num_threads, [] {}) {}

◆ thread_pool() [3/4]

template<tp OptFlags = tp::none>
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
BS::thread_pool< OptFlags >::thread_pool ( F &&  init)
inlineexplicit

Construct a new thread pool with the specified initialization function and the default number of threads.

Parameters
initAn initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function.
1472: thread_pool(0, std::forward<F>(init)) {}

◆ thread_pool() [4/4]

template<tp OptFlags = tp::none>
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
BS::thread_pool< OptFlags >::thread_pool ( const std::size_t  num_threads,
F &&  init 
)
inline

Construct a new thread pool with the specified number of threads and initialization function.

Parameters
num_threadsThe number of threads to use.
initAn initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function.
1482 {
1483 create_threads(num_threads, std::forward<F>(init));
1484 }
void create_threads(const std::size_t num_threads, F &&init)
Create the threads in the pool and assign a worker to each thread.
Definition BS_thread_pool.hpp:2064

◆ ~thread_pool()

template<tp OptFlags = tp::none>
BS::thread_pool< OptFlags >::~thread_pool ( )
inlinenoexcept

Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. If a cleanup function was set, it will run in each thread right before it is destroyed. Note that if the pool is paused, then any tasks still in the queue will never be executed.

1496 {
1497#ifdef __cpp_exceptions
1498 try
1499 {
1500#endif
1501 wait();
1502#ifndef __cpp_lib_jthread
1504#endif
1505#ifdef __cpp_exceptions
1506 }
1507 catch (...)
1508 {
1509 }
1510#endif
1511 }
void wait()
Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are curr...
Definition BS_thread_pool.hpp:1964
void destroy_threads()
Destroy the threads in the pool.
Definition BS_thread_pool.hpp:2108

Member Function Documentation

◆ detach_blocks()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void BS::thread_pool< OptFlags >::detach_blocks ( const T1  first_index,
const T2  index_after_last,
F &&  block,
const std::size_t  num_blocks = 0,
const priority_t  priority = 0 
)
inline

Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The block function takes two arguments, the start and end of the block, so that it is only called once per block, but it is up to the user to make sure the block function correctly deals with all the indices in each block. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the block function.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted.
blockA function that will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. block(start, end) should typically involve a loop of the form for (T i = start; i < end; ++i). Must not return a value.
num_blocksThe maximum number of blocks to split the loop into. The default is 0, which means the number of blocks will be equal to the number of threads in the pool.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1532 {
1533 enqueue_blocks<T, F, void, false>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(block), num_blocks, priority);
1534 }
@ priority
Enable task priority.
Here is the caller graph for this function:

◆ detach_bulk() [1/2]

template<tp OptFlags = tp::none>
template<typename I >
void BS::thread_pool< OptFlags >::detach_bulk ( const I  first,
const I  last,
const priority_t  priority = 0 
)
inline

Submit an iterator range containing functions with no arguments and no return values into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.

Template Parameters
IThe type of the iterators.
Parameters
firstAn iterator to the first function.
lastAn iterator to one past the last function.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1546 {
1547 if (first != last)
1548 {
1549 bool notify = false;
1550 {
1551 const std::scoped_lock tasks_lock(tasks_mutex);
1552 if constexpr (pause_enabled)
1553 notify = tasks.empty() && !paused;
1554 else
1555 notify = tasks.empty();
1556 for (I it = first; it != last; ++it)
1557 {
1558 if constexpr (priority_enabled)
1559 tasks.emplace(std::move(*it), priority);
1560 else
1561 tasks.emplace(std::move(*it));
1562 }
1563 }
1564 if (notify)
1565 task_available_cv.notify_all();
1566 }
1567 }
std::mutex tasks_mutex
A mutex to synchronize access to the task queue by different threads.
Definition BS_thread_pool.hpp:2341
static constexpr bool priority_enabled
A flag indicating whether task priority is enabled.
Definition BS_thread_pool.hpp:1434
std::condition_variable task_available_cv
A condition variable to notify worker() that a new task has become available.
Definition BS_thread_pool.hpp:2351
std::conditional_t< pause_enabled, bool, std::monostate > paused
A flag indicating whether the workers should pause. When set to true, the workers temporarily stop re...
Definition BS_thread_pool.hpp:2391
std::conditional_t< priority_enabled, std::priority_queue< pr_task >, std::queue< task_t > > tasks
A queue of tasks to be executed by the threads.
Definition BS_thread_pool.hpp:2371
static constexpr bool pause_enabled
A flag indicating whether pausing is enabled.
Definition BS_thread_pool.hpp:1439

◆ detach_bulk() [2/2]

template<tp OptFlags = tp::none>
template<typename C >
void BS::thread_pool< OptFlags >::detach_bulk ( C &  container,
const priority_t  priority = 0 
)
inline

Submit a container of functions with no arguments and no return values into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.

Template Parameters
CThe type of the container. Must either be an array or have begin() and end() member functions.
Parameters
containerThe container.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1578 {
1579 detach_bulk(std::begin(container), std::end(container), priority);
1580 }
void detach_bulk(const I first, const I last, const priority_t priority=0)
Submit an iterator range containing functions with no arguments and no return values into the task qu...
Definition BS_thread_pool.hpp:1545

◆ detach_loop()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void BS::thread_pool< OptFlags >::detach_loop ( const T1  first_index,
const T2  index_after_last,
F &&  loop,
const std::size_t  num_blocks = 0,
const priority_t  priority = 0 
)
inline

Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The loop function takes one argument, the loop index, and it is called exactly once per index, but many times per block. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the loop finishes executing, otherwise bad things will happen.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the loop function.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted.
loopA function that will be called once per index, many times per block. Should take exactly one argument: the loop index. Must not return a value.
num_blocksThe maximum number of blocks to split the loop into. The default is 0, which means the number of blocks will be equal to the number of threads in the pool.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1597 {
1598 enqueue_loop<T, F, false>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(loop), num_blocks, priority);
1599 }

◆ detach_sequence()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
void BS::thread_pool< OptFlags >::detach_sequence ( const T1  first_index,
const T2  index_after_last,
F &&  sequence,
const priority_t  priority = 0 
)
inline

Submit a sequence of tasks enumerated by indices to the queue, with the specified priority. The sequence function takes one argument, the task index, and will be called once per index. Does not return a BS::multi_future, so the user must use wait() or some other method to ensure that the sequence finishes executing, otherwise bad things will happen.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the sequence function.
Parameters
first_indexThe first index in the sequence.
index_after_lastThe index after the last index in the sequence. The sequence will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted.
sequenceA function that will be called once per index. Should take exactly one argument, the index. Must not return a value.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1615 {
1616 return enqueue_sequence<T, F, void, false>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(sequence), priority);
1617 }

◆ detach_task()

template<tp OptFlags = tp::none>
template<typename F >
void BS::thread_pool< OptFlags >::detach_task ( F &&  task,
const priority_t  priority = 0 
)
inline

Submit a function with no arguments and no return value into the task queue, with the specified priority. To submit a function with arguments, enclose it in a lambda expression. Does not return a future, so the user must use wait() or some other method to ensure that the task finishes executing, otherwise bad things will happen.

Template Parameters
FThe type of the function.
Parameters
taskThe function to submit.
priorityThe priority of the task. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
1628 {
1629 {
1630 const std::scoped_lock tasks_lock(tasks_mutex);
1631 if constexpr (priority_enabled)
1632 tasks.emplace(std::forward<F>(task), priority);
1633 else
1634 tasks.emplace(std::forward<F>(task));
1635 }
1636 task_available_cv.notify_one();
1637 }
Here is the caller graph for this function:

◆ get_tasks_queued()

template<tp OptFlags = tp::none>
std::size_t BS::thread_pool< OptFlags >::get_tasks_queued ( ) const
inline

Get the number of tasks currently waiting in the queue to be executed by the threads.

Returns
The number of queued tasks.
1660 {
1661 const std::scoped_lock tasks_lock(tasks_mutex);
1662 return tasks.size();
1663 }
Here is the caller graph for this function:

◆ get_tasks_running()

template<tp OptFlags = tp::none>
std::size_t BS::thread_pool< OptFlags >::get_tasks_running ( ) const
inline

Get the number of tasks currently being executed by the threads.

Returns
The number of running tasks.
1671 {
1672 const std::scoped_lock tasks_lock(tasks_mutex);
1673 return tasks_running;
1674 }
std::size_t tasks_running
A counter for the total number of currently running tasks.
Definition BS_thread_pool.hpp:2376

◆ get_tasks_total()

template<tp OptFlags = tp::none>
std::size_t BS::thread_pool< OptFlags >::get_tasks_total ( ) const
inline

Get the total number of unfinished tasks: either still waiting in the queue, or running in a thread. Note that get_tasks_total() == get_tasks_queued() + get_tasks_running().

Returns
The total number of tasks.
1682 {
1683 const std::scoped_lock tasks_lock(tasks_mutex);
1684 return tasks_running + tasks.size();
1685 }
Here is the caller graph for this function:

◆ get_thread_count()

template<tp OptFlags = tp::none>
std::size_t BS::thread_pool< OptFlags >::get_thread_count ( ) const
inlinenoexcept

Get the number of threads in the pool.

Returns
The number of threads.
1693 {
1694 return thread_count;
1695 }
std::size_t thread_count
The number of threads in the pool.
Definition BS_thread_pool.hpp:2381
Here is the caller graph for this function:

◆ get_thread_ids()

template<tp OptFlags = tp::none>
std::vector< thread_t::id > BS::thread_pool< OptFlags >::get_thread_ids ( ) const
inline

Get a vector containing the unique identifiers for each of the pool's threads, as obtained by std::thread::get_id() (or std::jthread::get_id() in C++20 and later).

Returns
The unique thread identifiers.
1703 {
1704 std::vector<thread_t::id> thread_ids(thread_count);
1705 for (std::size_t i = 0; i < thread_count; ++i)
1706 thread_ids[i] = threads[i].get_id();
1707 return thread_ids;
1708 }
std::unique_ptr< thread_t[]> threads
A smart pointer to manage the memory allocated for the threads.
Definition BS_thread_pool.hpp:2386

◆ is_paused()

template<tp OptFlags = tp::none>
BS_THREAD_POOL_IF_PAUSE_ENABLED bool BS::thread_pool< OptFlags >::is_paused ( ) const
inline

Check whether the pool is currently paused. Only enabled if the flag BS::tp::pause is enabled in the template parameter.

Returns
true if the pool is paused, false if it is not paused.
1717 {
1718 const std::scoped_lock tasks_lock(tasks_mutex);
1719 return paused;
1720 }

◆ pause()

template<tp OptFlags = tp::none>
BS_THREAD_POOL_IF_PAUSE_ENABLED void BS::thread_pool< OptFlags >::pause ( )
inline

Pause the pool. The workers will temporarily stop retrieving new tasks out of the queue, although any tasks already executing will keep running until they are finished. Only enabled if the flag BS::tp::pause is enabled in the template parameter.

1727 {
1728 const std::scoped_lock tasks_lock(tasks_mutex);
1729 paused = true;
1730 }
Here is the caller graph for this function:

◆ purge()

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::purge ( )
inline

Purge all the tasks waiting in the queue. Tasks that are currently running will not be affected, but any tasks still waiting in the queue will be discarded, and will never be executed by the threads. Please note that there is no way to restore the purged tasks.

1736 {
1737 const std::scoped_lock tasks_lock(tasks_mutex);
1738 tasks = {};
1739 }
Here is the caller graph for this function:

◆ reset() [1/4]

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::reset ( )
inline

Reset the pool with the default number of threads (as if constructed with the default constructor). Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.

1745 {
1746 reset(0, [](std::size_t) {});
1747 }
void reset()
Reset the pool with the default number of threads (as if constructed with the default constructor)....
Definition BS_thread_pool.hpp:1744
Here is the caller graph for this function:

◆ reset() [2/4]

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::reset ( const std::size_t  num_threads)
inline

Reset the pool with a new number of threads. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.

Parameters
num_threadsThe number of threads to use.
1755 {
1756 reset(num_threads, [](std::size_t) {});
1757 }

◆ reset() [3/4]

template<tp OptFlags = tp::none>
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void BS::thread_pool< OptFlags >::reset ( F &&  init)
inline

Reset the pool with the default number of threads and a new initialization function. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.

Parameters
initAn initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function.
1766 {
1767 reset(0, std::forward<F>(init));
1768 }

◆ reset() [4/4]

template<tp OptFlags = tp::none>
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void BS::thread_pool< OptFlags >::reset ( const std::size_t  num_threads,
F &&  init 
)
inline

Reset the pool with a new number of threads and a new initialization function. Waits for all tasks to be completed, both running and queued, then destroys the thread pool and creates a new one with an empty task queue. If pausing is enabled, only waits for tasks that are currently running before destroying the pool; once the pool is reset, it will then resume executing the tasks that remained in the queue and any newly submitted tasks. If the pool was paused before resetting it, the new pool will be paused as well.

Parameters
num_threadsThe number of threads to use.
initAn initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function.
1778 {
1779 if constexpr (pause_enabled)
1780 {
1781 std::unique_lock tasks_lock(tasks_mutex);
1782 const bool was_paused = paused;
1783 paused = true;
1784 tasks_lock.unlock();
1785 reset_pool(num_threads, std::forward<F>(init));
1786 tasks_lock.lock();
1787 paused = was_paused;
1788 tasks_lock.unlock();
1789 if (!was_paused)
1790 task_available_cv.notify_all();
1791 }
1792 else
1793 {
1794 reset_pool(num_threads, std::forward<F>(init));
1795 }
1796 }
void reset_pool(const std::size_t num_threads, F &&init)
Reset the pool with a new number of threads and a new initialization function. This member function i...
Definition BS_thread_pool.hpp:2269

◆ set_cleanup_func()

template<tp OptFlags = tp::none>
template<BS_THREAD_POOL_INIT_FUNC_CONCEPT(F) >
void BS::thread_pool< OptFlags >::set_cleanup_func ( F &&  cleanup)
inline

Set the thread pool's cleanup function.

Parameters
cleanupA cleanup function to run in each thread right before it is destroyed, which will happen when the pool is destructed or reset. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. The cleanup function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function.
1805 {
1806 if constexpr (std::is_invocable_v<F, std::size_t>)
1807 {
1808 cleanup_func = std::forward<F>(cleanup);
1809 }
1810 else
1811 {
1812 cleanup_func = [cleanup = std::forward<F>(cleanup)](std::size_t)
1813 {
1814 cleanup();
1815 };
1816 }
1817 }
move_only_function< void(std::size_t)> cleanup_func
A cleanup function to run in each thread right before it is destroyed, which will happen when the poo...
Definition BS_thread_pool.hpp:2361

◆ submit_blocks()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F , typename R = std::invoke_result_t<std::decay_t<F>, T, T>>
multi_future< R > BS::thread_pool< OptFlags >::submit_blocks ( const T1  first_index,
const T2  index_after_last,
F &&  block,
const std::size_t  num_blocks = 0,
const priority_t  priority = 0 
)
inline

Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The block function takes two arguments, the start and end of the block, so that it is only called once per block, but it is up to the user to make sure the block function correctly deals with all the indices in each block. If the block function has a return value, get a BS::multi_future for the eventual returned values. If the block function has no return value, get a BS::multi_future<void> which can be used to wait until all the tasks finish.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the block function.
RThe return type of the block function (can be void).
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted, and an empty BS::multi_future will be returned.
blockA function that will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. block(start, end) should typically involve a loop of the form for (T i = start; i < end; ++i). Can return a value.
num_blocksThe maximum number of blocks to split the loop into. The default is 0, which means the number of blocks will be equal to the number of threads in the pool.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A BS::multi_future that can be used to wait for all the tasks to finish. If the block function returns a value, the BS::multi_future can also be used to obtain the values returned by each block.
1836 {
1837 return enqueue_blocks<T, F, R, true>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(block), num_blocks, priority);
1838 }

◆ submit_bulk() [1/2]

template<tp OptFlags = tp::none>
template<typename I , typename F = decltype(*std::declval<I>()), typename R = std::invoke_result_t<std::decay_t<F>>>
multi_future< R > BS::thread_pool< OptFlags >::submit_bulk ( const I  first,
const I  last,
const priority_t  priority = 0 
)
inline

Submit an iterator range containing functions with no arguments into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. If the functions have return values, get a BS::multi_future for the eventual returned values. If the functions have no return values, get a BS::multi_future<void> which can be used to wait until all the tasks finish.

Template Parameters
IThe type of the iterators.
FThe type of the functions.
RThe return type of the functions (can be void, but must be the same for all the functions).
Parameters
firstAn iterator to the first function.
lastAn iterator to one past the last function.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A BS::multi_future that can be used to wait for all the tasks to finish. If the functions return values, the BS::multi_future can also be used to obtain the values returned by each task.
1853 {
1854 if (first != last)
1855 {
1856 const std::size_t num_tasks = static_cast<std::size_t>(std::distance(first, last));
1857 multi_future<R> all_futures;
1858 all_futures.reserve(num_tasks);
1859 std::vector<task_t> all_tasks;
1860 all_tasks.reserve(num_tasks);
1861 for (I it = first; it != last; ++it)
1862 {
1863 task_and_future<R> ft(std::move(*it));
1864 all_futures.emplace_back(std::move(ft.future));
1865 all_tasks.emplace_back(std::move(ft.task));
1866 }
1867 detach_bulk(all_tasks, priority);
1868 return all_futures;
1869 }
1870 return {};
1871 }

◆ submit_bulk() [2/2]

template<tp OptFlags = tp::none>
template<typename C , typename F = decltype(*std::declval<C&>().begin()), typename R = std::invoke_result_t<std::decay_t<F>>>
multi_future< R > BS::thread_pool< OptFlags >::submit_bulk ( C &  container,
const priority_t  priority = 0 
)
inline

Submit a container of functions with no arguments into the task queue, with the specified priority. To submit functions with arguments, enclose them in lambda expressions. If the functions have return values, get a BS::multi_future for the eventual returned values. If the functions have no return values, get a BS::multi_future<void> which can be used to wait until all the tasks finish.

Template Parameters
CThe type of the container. Must either be an array or have begin() and end() member functions.
FThe type of the functions.
RThe return type of the functions (can be void, but must be the same for all the functions).
Parameters
containerThe container.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A BS::multi_future that can be used to wait for all the tasks to finish. If the functions return values, the BS::multi_future can also be used to obtain the values returned by each task.
1885 {
1886 return submit_bulk(std::begin(container), std::end(container), priority);
1887 }
multi_future< R > submit_bulk(const I first, const I last, const priority_t priority=0)
Submit an iterator range containing functions with no arguments into the task queue,...
Definition BS_thread_pool.hpp:1852

◆ submit_loop()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F >
multi_future< void > BS::thread_pool< OptFlags >::submit_loop ( const T1  first_index,
const T2  index_after_last,
F &&  loop,
const std::size_t  num_blocks = 0,
const priority_t  priority = 0 
)
inline

Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue, with the specified priority. The loop function takes one argument, the loop index, and it is called exactly once per index, but many times per block. Returns a BS::multi_future which can be used to wait until all the tasks finish.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the loop function.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted, and an empty BS::multi_future will be returned.
loopA function that will be called once per index, many times per block. Should take exactly one argument: the loop index. Must not return a value.
num_blocksThe maximum number of blocks to split the loop into. The default is 0, which means the number of blocks will be equal to the number of threads in the pool.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A BS::multi_future that can be used to wait for all the tasks to finish.
1905 {
1906 return enqueue_loop<T, F, true>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(loop), num_blocks, priority);
1907 }

◆ submit_sequence()

template<tp OptFlags = tp::none>
template<typename T1 , typename T2 , typename T = common_index_type_t<T1, T2>, typename F , typename R = std::invoke_result_t<std::decay_t<F>, T>>
multi_future< R > BS::thread_pool< OptFlags >::submit_sequence ( const T1  first_index,
const T2  index_after_last,
F &&  sequence,
const priority_t  priority = 0 
)
inline

Submit a sequence of tasks enumerated by indices to the queue, with the specified priority. The sequence function takes one argument, the task index, and will be called once per index. If the sequence function has a return value, get a BS::multi_future for the eventual returned values. If the sequence function has no return value, get a BS::multi_future<void> which can be used to wait until all the tasks finish.

Template Parameters
T1The type of the first index. Should be a signed or unsigned integer.
T2The type of the index after the last index. Should be a signed or unsigned integer.
TThe common type of the indices, as determined by BS::common_index_type_t<T1, T2>.
FThe type of the sequence function.
RThe return type of the sequence function (can be void).
Parameters
first_indexThe first index in the sequence.
index_after_lastThe index after the last index in the sequence. The sequence will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to for (T i = first_index; i < index_after_last; ++i). Note that if index_after_last <= first_index, no tasks will be submitted, and an empty BS::multi_future will be returned.
sequenceA function that will be called once per index. Should take exactly one argument, the index. Can return a value.
priorityThe priority of the tasks. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A BS::multi_future that can be used to wait for all the tasks to finish. If the sequence function returns a value, the BS::multi_future can also be used to obtain the values returned by each task.
1925 {
1926 return enqueue_sequence<T, F, R, true>(static_cast<T>(first_index), static_cast<T>(index_after_last), std::forward<F>(sequence), priority);
1927 }

◆ submit_task()

template<tp OptFlags = tp::none>
template<typename F , typename R = std::invoke_result_t<std::decay_t<F>>>
std::future< R > BS::thread_pool< OptFlags >::submit_task ( F &&  task,
const priority_t  priority = 0 
)
inline

Submit a function with no arguments into the task queue, with the specified priority. To submit a function with arguments, enclose it in a lambda expression. If the function has a return value, get a future for the eventual returned value. If the function has no return value, get an std::future<void> which can be used to wait until the task finishes.

Template Parameters
FThe type of the function.
RThe return type of the function (can be void).
Parameters
taskThe function to submit.
priorityThe priority of the task. Should be between -128 and +127 (a signed 8-bit integer). The default is 0. Only taken into account if the flag BS::tp::priority is enabled in the template parameter, otherwise has no effect.
Returns
A future to be used later to wait for the function to finish executing and/or obtain its returned value if it has one.
1940 {
1941 task_and_future<R> ft(std::forward<F>(task));
1942 detach_task(std::move(ft.task), priority);
1943 return std::move(ft.future);
1944 }
void detach_task(F &&task, const priority_t priority=0)
Submit a function with no arguments and no return value into the task queue, with the specified prior...
Definition BS_thread_pool.hpp:1627
Here is the caller graph for this function:

◆ unpause()

template<tp OptFlags = tp::none>
BS_THREAD_POOL_IF_PAUSE_ENABLED void BS::thread_pool< OptFlags >::unpause ( )
inline

Unpause the pool. The workers will resume retrieving new tasks out of the queue. Only enabled if the flag BS::tp::pause is enabled in the template parameter.

1951 {
1952 {
1953 const std::scoped_lock tasks_lock(tasks_mutex);
1954 paused = false;
1955 }
1956 task_available_cv.notify_all();
1957 }
Here is the caller graph for this function:

◆ wait()

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::wait ( )
inline

Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the pool is paused, this function only waits for the currently running tasks (otherwise it would wait forever). Note: To wait for just one specific task, use submit_task() instead, and call the wait() member function of the generated future.

Exceptions
`wait_deadlock`if called from within a thread of the same pool, which would result in a deadlock. Only enabled if the flag BS::tp::wait_deadlock_checks is enabled in the template parameter.
1965 {
1966#ifdef __cpp_exceptions
1967 if constexpr (wait_deadlock_checks_enabled)
1968 {
1969 if (this_thread::get_pool() == this)
1970 throw wait_deadlock();
1971 }
1972#endif
1973 std::unique_lock tasks_lock(tasks_mutex);
1974 waiting = true;
1975 tasks_done_cv.wait(tasks_lock,
1976 [this]
1977 {
1978 if constexpr (pause_enabled)
1979 return (tasks_running == 0) && (paused || tasks.empty());
1980 else
1981 return (tasks_running == 0) && tasks.empty();
1982 });
1983 waiting = false;
1984 }
static std::optional< void * > get_pool() noexcept
Get a pointer to the thread pool that owns the current thread. If this thread belongs to a BS::thread...
Definition BS_thread_pool.hpp:987
std::condition_variable tasks_done_cv
A condition variable to notify wait() that the tasks are done.
Definition BS_thread_pool.hpp:2356
bool waiting
A flag indicating that wait() is active and expects to be notified whenever a task is done.
Definition BS_thread_pool.hpp:2396
static constexpr bool wait_deadlock_checks_enabled
A flag indicating whether wait deadlock checks are enabled.
Definition BS_thread_pool.hpp:1444
Here is the caller graph for this function:

◆ wait_for()

template<tp OptFlags = tp::none>
template<typename R , typename P >
bool BS::thread_pool< OptFlags >::wait_for ( const std::chrono::duration< R, P > &  duration)
inline

Wait for tasks to be completed, but stop waiting after the specified duration has passed.

Template Parameters
RAn arithmetic type representing the number of ticks to wait.
PAn std::ratio representing the length of each tick in seconds.
Parameters
durationThe amount of time to wait.
Returns
true if all tasks finished running, false if the duration expired but some tasks are still running.
Exceptions
`wait_deadlock`if called from within a thread of the same pool, which would result in a deadlock. Only enabled if the flag BS::tp::wait_deadlock_checks is enabled in the template parameter.
1997 {
1998#ifdef __cpp_exceptions
1999 if constexpr (wait_deadlock_checks_enabled)
2000 {
2001 if (this_thread::get_pool() == this)
2002 throw wait_deadlock();
2003 }
2004#endif
2005 std::unique_lock tasks_lock(tasks_mutex);
2006 waiting = true;
2007 const bool status = tasks_done_cv.wait_for(tasks_lock, duration,
2008 [this]
2009 {
2010 if constexpr (pause_enabled)
2011 return (tasks_running == 0) && (paused || tasks.empty());
2012 else
2013 return (tasks_running == 0) && tasks.empty();
2014 });
2015 waiting = false;
2016 return status;
2017 }

◆ wait_until()

template<tp OptFlags = tp::none>
template<typename C , typename D >
bool BS::thread_pool< OptFlags >::wait_until ( const std::chrono::time_point< C, D > &  timeout_time)
inline

Wait for tasks to be completed, but stop waiting after the specified time point has been reached.

Template Parameters
CThe type of the clock used to measure time.
DAn std::chrono::duration type used to indicate the time point.
Parameters
timeout_timeThe time point at which to stop waiting.
Returns
true if all tasks finished running, false if the time point was reached but some tasks are still running.
Exceptions
`wait_deadlock`if called from within a thread of the same pool, which would result in a deadlock. Only enabled if the flag BS::tp::wait_deadlock_checks is enabled in the template parameter.
2030 {
2031#ifdef __cpp_exceptions
2032 if constexpr (wait_deadlock_checks_enabled)
2033 {
2034 if (this_thread::get_pool() == this)
2035 throw wait_deadlock();
2036 }
2037#endif
2038 std::unique_lock tasks_lock(tasks_mutex);
2039 waiting = true;
2040 const bool status = tasks_done_cv.wait_until(tasks_lock, timeout_time,
2041 [this]
2042 {
2043 if constexpr (pause_enabled)
2044 return (tasks_running == 0) && (paused || tasks.empty());
2045 else
2046 return (tasks_running == 0) && tasks.empty();
2047 });
2048 waiting = false;
2049 return status;
2050 }

◆ create_threads()

template<tp OptFlags = tp::none>
template<typename F >
void BS::thread_pool< OptFlags >::create_threads ( const std::size_t  num_threads,
F &&  init 
)
inlineprivate

Create the threads in the pool and assign a worker to each thread.

Parameters
num_threadsThe number of threads to use.
initAn initialization function to run in each thread before it starts executing any submitted tasks.
2065 {
2066 if constexpr (std::is_invocable_v<F, std::size_t>)
2067 {
2068 init_func = std::forward<F>(init);
2069 }
2070 else
2071 {
2072 init_func = [init = std::forward<F>(init)](std::size_t)
2073 {
2074 init();
2075 };
2076 }
2077 thread_count = determine_thread_count(num_threads);
2078 threads = std::make_unique<thread_t[]>(thread_count);
2079 {
2080 const std::scoped_lock tasks_lock(tasks_mutex);
2082#ifndef __cpp_lib_jthread
2083 workers_running = true;
2084#endif
2085 }
2086 for (std::size_t i = 0; i < thread_count; ++i)
2087 {
2088 threads[i] = thread_t(
2089 [this, i]
2090#ifdef __cpp_lib_jthread
2091 (const std::stop_token& stop_token)
2092 {
2093 worker(stop_token, i);
2094 }
2095#else
2096 {
2097 worker(i);
2098 }
2099#endif
2100 );
2101 }
2102 }
void worker(BS_THREAD_POOL_WORKER_TOKEN const std::size_t idx)
A worker function to be assigned to each thread in the pool. Waits until it is notified by detach_tas...
Definition BS_thread_pool.hpp:2283
static std::size_t determine_thread_count(const std::size_t num_threads) noexcept(!thread_pool_native_extensions)
Determine how many threads the pool should have, based on the parameter passed to the constructor or ...
Definition BS_thread_pool.hpp:2125
move_only_function< void(std::size_t)> init_func
An initialization function to run in each thread before it starts executing any submitted tasks....
Definition BS_thread_pool.hpp:2366
bool workers_running
A flag indicating to the workers to keep running. When set to false, the workers terminate permanentl...
Definition BS_thread_pool.hpp:2402
std::thread thread_t
The type of threads to use. In C++17 we use std::thread.
Definition BS_thread_pool.hpp:381

◆ destroy_threads()

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::destroy_threads ( )
inlineprivate

Destroy the threads in the pool.

2109 {
2110 {
2111 const std::scoped_lock tasks_lock(tasks_mutex);
2112 workers_running = false;
2113 }
2114 task_available_cv.notify_all();
2115 for (std::size_t i = 0; i < thread_count; ++i)
2116 threads[i].join();
2117 }
cv::String join(const cv::String &base, const cv::String &path)
Here is the call graph for this function:

◆ determine_thread_count()

template<tp OptFlags = tp::none>
static std::size_t BS::thread_pool< OptFlags >::determine_thread_count ( const std::size_t  num_threads)
inlinestaticprivatenoexcept

Determine how many threads the pool should have, based on the parameter passed to the constructor or reset().

Parameters
num_threadsThe parameter passed to the constructor or reset(). If the parameter is a positive number, then the pool will be created with this number of threads. If the parameter is zero, or a parameter was not supplied (in which case it will have the default value of 0), then the pool will be created with the total number of hardware threads available, as obtained from thread_t::hardware_concurrency(). If the latter returns zero for some reason, then the pool will be created with just one thread. If the native extensions are enabled, the pool will instead use the number of threads available to the process, as obtained from BS::get_os_process_affinity(), which can be less than the number of hardware threads.
2126 {
2127 if (num_threads > 0)
2128 return num_threads;
2129#ifdef BS_THREAD_POOL_NATIVE_EXTENSIONS
2130 const std::optional<std::vector<bool>> affinity = BS::get_os_process_affinity();
2131 if (affinity.has_value())
2132 {
2133 const std::size_t affinity_thread_count = static_cast<std::size_t>(std::count(affinity->begin(), affinity->end(), true));
2134 return (affinity_thread_count > 0) ? affinity_thread_count : 1;
2135 }
2136#endif
2137 if (thread_t::hardware_concurrency() > 0)
2138 return thread_t::hardware_concurrency();
2139 return 1;
2140 }

◆ enqueue_blocks()

template<tp OptFlags = tp::none>
template<typename T , typename F , typename R , bool submit, typename N = std::conditional_t<submit, multi_future<R>, void>>
N BS::thread_pool< OptFlags >::enqueue_blocks ( const T  first_index,
const T  index_after_last,
F &&  block,
std::size_t  num_blocks,
const priority_t  priority = 0 
)
inlineprivate

A helper function for detach_blocks() and submit_blocks().

Template Parameters
TThe type of the indices.
FThe type of the block function.
RThe return type of the block function (can be void).
submittrue if called from submit_blocks(), false if called from detach_blocks().
NThe return type of this helper function.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop.
blockA function that will be called once per block.
num_blocksThe maximum number of blocks to split the loop into.
priorityThe priority of the tasks.
Returns
A BS::multi_future if submit is true, or void if submit is false.
2159 {
2160 if (index_after_last > first_index)
2161 {
2162 using block_task_t = block_task<T, F, R>;
2163 const std::shared_ptr<std::decay_t<F>> block_ptr = std::make_shared<std::decay_t<F>>(std::forward<F>(block));
2164 const blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count);
2165 num_blocks = blks.get_num_blocks();
2166 std::vector<std::conditional_t<submit, block_task_t, task_t>> all_tasks;
2167 all_tasks.reserve(num_blocks);
2168 for (std::size_t i = 0; i < num_blocks; ++i)
2169 all_tasks.emplace_back(block_task_t{block_ptr, blks.start(i), blks.end(i)});
2170 if constexpr (submit)
2171 return submit_bulk(all_tasks, priority);
2172 else
2173 detach_bulk(all_tasks, priority);
2174 }
2175 return N();
2176 }
Here is the call graph for this function:

◆ enqueue_loop()

template<tp OptFlags = tp::none>
template<typename T , typename F , bool submit, typename N = std::conditional_t<submit, multi_future<void>, void>>
N BS::thread_pool< OptFlags >::enqueue_loop ( const T  first_index,
const T  index_after_last,
F &&  loop,
std::size_t  num_blocks,
const priority_t  priority = 0 
)
inlineprivate

A helper function for detach_loop() and submit_loop().

Template Parameters
TThe type of the indices.
FThe type of the loop function.
submittrue if called from submit_loop(), false if called from detach_loop().
NThe return type of this helper function.
Parameters
first_indexThe first index in the loop.
index_after_lastThe index after the last index in the loop.
loopA function that will be called once per index, many times per block.
num_blocksThe maximum number of blocks to split the loop into.
priorityThe priority of the tasks.
Returns
A BS::multi_future if submit is true, or void if submit is false.
2194 {
2195 if (index_after_last > first_index)
2196 {
2197 using loop_task_t = loop_task<T, F>;
2198 const std::shared_ptr<std::decay_t<F>> loop_ptr = std::make_shared<std::decay_t<F>>(std::forward<F>(loop));
2199 const blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count);
2200 num_blocks = blks.get_num_blocks();
2201 std::vector<std::conditional_t<submit, loop_task_t, task_t>> all_tasks;
2202 all_tasks.reserve(num_blocks);
2203 for (std::size_t i = 0; i < num_blocks; ++i)
2204 all_tasks.emplace_back(loop_task_t{loop_ptr, blks.start(i), blks.end(i)});
2205 if constexpr (submit)
2206 return submit_bulk(all_tasks, priority);
2207 else
2208 detach_bulk(all_tasks, priority);
2209 }
2210 return N();
2211 }
Here is the call graph for this function:

◆ enqueue_sequence()

template<tp OptFlags = tp::none>
template<typename T , typename F , typename R , bool submit, typename N = std::conditional_t<submit, multi_future<R>, void>>
N BS::thread_pool< OptFlags >::enqueue_sequence ( const T  first_index,
const T  index_after_last,
F &&  sequence,
const priority_t  priority = 0 
)
inlineprivate

A helper function for detach_sequence() and submit_sequence().

Template Parameters
TThe type of the indices.
FThe type of the sequence function.
RThe return type of the sequence function (can be void).
submittrue if called from submit_sequence(), false if called from detach_sequence().
NThe return type of this helper function.
Parameters
first_indexThe first index in the sequence.
index_after_lastThe index after the last index in the sequence.
sequenceA function that will be called once per index.
priorityThe priority of the tasks.
Returns
A BS::multi_future if submit is true, or void if submit is false.
2229 {
2230 if (index_after_last > first_index)
2231 {
2232 using sequence_task_t = sequence_task<T, F, R>;
2233 const std::shared_ptr<std::decay_t<F>> sequence_ptr = std::make_shared<std::decay_t<F>>(std::forward<F>(sequence));
2234 std::vector<std::conditional_t<submit, sequence_task_t, task_t>> all_tasks;
2235 all_tasks.reserve(static_cast<std::size_t>(index_after_last - first_index));
2236 for (T i = first_index; i < index_after_last; ++i)
2237 all_tasks.emplace_back(sequence_task_t{sequence_ptr, i});
2238 if constexpr (submit)
2239 return submit_bulk(all_tasks, priority);
2240 else
2241 detach_bulk(all_tasks, priority);
2242 }
2243 return N();
2244 }

◆ pop_task()

template<tp OptFlags = tp::none>
task_t BS::thread_pool< OptFlags >::pop_task ( )
inlineprivate

Pop a task from the queue.

Returns
The task.
2252 {
2253 task_t task;
2254 if constexpr (priority_enabled)
2255 task = std::move(tasks.top().task);
2256 else
2257 task = std::move(tasks.front());
2258 tasks.pop();
2259 return task;
2260 }
move_only_function< void()> task_t
The type of tasks in the task queue.
Definition BS_thread_pool.hpp:365

◆ reset_pool()

template<tp OptFlags = tp::none>
template<typename F >
void BS::thread_pool< OptFlags >::reset_pool ( const std::size_t  num_threads,
F &&  init 
)
inlineprivate

Reset the pool with a new number of threads and a new initialization function. This member function implements the actual reset, while the public member function reset() also handles the case where the pool is paused.

Parameters
num_threadsThe number of threads to use.
initAn initialization function to run in each thread before it starts executing any submitted tasks.
2270 {
2271 wait();
2272#ifndef __cpp_lib_jthread
2274#endif
2275 create_threads(num_threads, std::forward<F>(init));
2276 }

◆ worker()

template<tp OptFlags = tp::none>
void BS::thread_pool< OptFlags >::worker ( BS_THREAD_POOL_WORKER_TOKEN const std::size_t  idx)
inlineprivate

A worker function to be assigned to each thread in the pool. Waits until it is notified by detach_task() that a task is available, and then retrieves the task from the queue and executes it. Once the task finishes, the worker notifies wait() in case it is waiting.

Parameters
idxThe index of this thread.
2284 {
2285 this_thread::my_pool = this;
2286 this_thread::my_index = idx;
2287 init_func(idx);
2288 while (true)
2289 {
2290 std::unique_lock tasks_lock(tasks_mutex);
2291 --tasks_running;
2292 if constexpr (pause_enabled)
2293 {
2294 if (waiting && (tasks_running == 0) && (paused || tasks.empty()))
2295 tasks_done_cv.notify_all();
2296 }
2297 else
2298 {
2299 if (waiting && (tasks_running == 0) && tasks.empty())
2300 tasks_done_cv.notify_all();
2301 }
2302 task_available_cv.wait(tasks_lock BS_THREAD_POOL_WAIT_TOKEN,
2303 [this]
2304 {
2305 if constexpr (pause_enabled)
2306 return !(paused || tasks.empty()) BS_THREAD_POOL_OR_STOP_CONDITION;
2307 else
2308 return !tasks.empty() BS_THREAD_POOL_OR_STOP_CONDITION;
2309 });
2310 if (BS_THREAD_POOL_STOP_CONDITION)
2311 break;
2312 {
2313 task_t task = pop_task(); // NOLINT(misc-const-correctness) In C++23 this cannot be const since `std::move_only_function::operator()` is not a const member function.
2314 ++tasks_running;
2315 tasks_lock.unlock();
2316#ifdef __cpp_exceptions
2317 try
2318 {
2319#endif
2320 task();
2321#ifdef __cpp_exceptions
2322 }
2323 catch (...)
2324 {
2325 }
2326#endif
2327 }
2328 }
2329 cleanup_func(idx);
2330 this_thread::my_index = std::nullopt;
2331 this_thread::my_pool = std::nullopt;
2332 }
task_t pop_task()
Pop a task from the queue.
Definition BS_thread_pool.hpp:2251

Member Data Documentation

◆ cleanup_func

template<tp OptFlags = tp::none>
move_only_function<void(std::size_t)> BS::thread_pool< OptFlags >::cleanup_func = [](std::size_t) {}
private

A cleanup function to run in each thread right before it is destroyed, which will happen when the pool is destructed or reset. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. The cleanup function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function. The default is an empty function, i.e., no cleanup will be performed.

2361{};

◆ init_func

template<tp OptFlags = tp::none>
move_only_function<void(std::size_t)> BS::thread_pool< OptFlags >::init_func = [](std::size_t) {}
private

An initialization function to run in each thread before it starts executing any submitted tasks. The function must have no return value, and can either take one argument, the thread index of type std::size_t, or zero arguments. It will be executed exactly once per thread, when the thread is first constructed. The initialization function must not throw any exceptions, as that will result in program termination. Any exceptions must be handled explicitly within the function. The default is an empty function, i.e., no initialization will be performed.

2366{};

◆ paused

template<tp OptFlags = tp::none>
std::conditional_t<pause_enabled, bool, std::monostate> BS::thread_pool< OptFlags >::paused = {}
private

A flag indicating whether the workers should pause. When set to true, the workers temporarily stop retrieving new tasks out of the queue, although any tasks already executing will keep running until they are finished. When set to false again, the workers resume retrieving tasks. Only enabled if the flag BS::tp::pause is enabled in the template parameter.

2391{};

The documentation for this class was generated from the following file: