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