Botan 3.13.0
Crypto and TLS for C&
thread_pool.cpp
Go to the documentation of this file.
1/*
2* (C) 2019,2021 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/thread_pool.h>
8
9#include <botan/exceptn.h>
10#include <botan/internal/os_utils.h>
11#include <botan/internal/parsing.h>
12#include <botan/internal/target_info.h>
13#include <algorithm>
14#include <thread>
15
16namespace Botan {
17
18namespace {
19
20std::optional<size_t> global_thread_pool_size() {
21 std::string var;
22 if(OS::read_env_variable(var, "BOTAN_THREAD_POOL_SIZE")) {
23 if(var == "none") {
24 return std::nullopt;
25 }
26
27 // Try to convert to an integer if possible:
28 if(const auto sz = parse_sz(var)) {
29 return sz;
30 }
31
32 // If it was neither a number nor a special value, then ignore the env
33 }
34
35 /*
36 * On a few platforms, disable the thread pool by default; it is only
37 * used if a size is set explicitly in the environment.
38 */
39
40#if defined(BOTAN_TARGET_OS_IS_MINGW)
41 // MinGW seems to have bugs causing deadlock on application exit.
42 // See https://github.com/randombit/botan/issues/2582 for background.
43 return std::nullopt;
44#elif defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
45 // Emscripten's threads are reportedly problematic
46 // See https://github.com/randombit/botan/issues/4195
47 return std::nullopt;
48#else
49 // Some(0) means choose based on CPU count
50 return std::optional<size_t>(0);
51#endif
52}
53
54} // namespace
55
56//static
58 static Thread_Pool g_thread_pool(global_thread_pool_size());
59 return g_thread_pool;
60}
61
62Thread_Pool::Thread_Pool(std::optional<size_t> opt_pool_size) : m_shutdown(false) {
63 // On Linux, it is 16 length max, including terminator
64 const std::string tname = "Botan thread";
65
66 if(!opt_pool_size.has_value()) {
67 return;
68 }
69
70 size_t pool_size = opt_pool_size.value();
71
72 if(pool_size == 0) {
73 /*
74 * For large machines don't create too many threads, unless
75 * explicitly asked to by the caller.
76 */
77 const size_t cores = OS::get_cpu_available();
78 pool_size = std::clamp<size_t>(cores, 2, 16);
79 }
80
81 m_workers.resize(pool_size);
82
83 for(size_t i = 0; i != pool_size; ++i) {
84 m_workers[i] = std::thread(&Thread_Pool::worker_thread, this);
85 OS::set_thread_name(m_workers[i], tname);
86 }
87}
88
90 {
91 const std::unique_lock<std::mutex> lock(m_mutex);
92
93 if(m_shutdown) {
94 return;
95 }
96
97 m_shutdown = true;
98
99 m_more_tasks.notify_all();
100 }
101
102 for(auto&& thread : m_workers) {
103 thread.join();
104 }
105 m_workers.clear();
106}
107
108void Thread_Pool::queue_thunk(const std::function<void()>& work) {
109 std::unique_lock<std::mutex> lock(m_mutex);
110
111 if(m_shutdown) {
112 throw Invalid_State("Cannot add work after thread pool has shut down");
113 }
114
115 if(m_workers.empty()) {
116 lock.unlock();
117 return work();
118 }
119
120 m_tasks.push_back(work);
121 m_more_tasks.notify_one();
122}
123
124void Thread_Pool::worker_thread() {
125 for(;;) {
126 std::function<void()> task;
127
128 {
129 std::unique_lock<std::mutex> lock(m_mutex);
130 m_more_tasks.wait(lock, [this] { return m_shutdown || !m_tasks.empty(); });
131
132 if(m_tasks.empty()) {
133 if(m_shutdown) {
134 return;
135 } else {
136 continue;
137 }
138 }
139
140 task = m_tasks.front();
141 m_tasks.pop_front();
142 }
143
144 task();
145 }
146}
147
148} // namespace Botan
Thread_Pool(std::optional< size_t > pool_size)
void queue_thunk(const std::function< void()> &work)
static Thread_Pool & global_instance()
bool read_env_variable(std::string &value_out, std::string_view var_name)
Definition os_utils.cpp:449
std::optional< size_t > parse_sz(std::string_view input, bool require_canonical)
Definition parsing.cpp:72
secure_vector< T > lock(const std::vector< T > &in)
Definition secmem.h:145