Coverage Report

Created: 2025-04-11 06:34

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