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