Coverage Report

Created: 2021-05-04 09:02

/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
#include <botan/internal/os_utils.h>
9
#include <botan/exceptn.h>
10
#include <thread>
11
12
namespace Botan {
13
14
namespace {
15
16
std::optional<size_t> global_thread_pool_size()
17
0
   {
18
0
   std::string var;
19
0
   if(OS::read_env_variable(var, "BOTAN_THREAD_POOL_SIZE"))
20
0
      {
21
0
      try
22
0
         {
23
0
         return std::optional<size_t>(std::stoul(var, nullptr));
24
0
         }
25
0
      catch(std::exception&) { /* ignore it */ }
26
27
0
      if(var == "none")
28
0
         return std::nullopt;
29
0
      }
30
31
   // If it was neither a number nor a special value, then ignore it.
32
0
   return std::optional<size_t>(0);
33
0
   }
34
35
}
36
37
//static
38
Thread_Pool& Thread_Pool::global_instance()
39
0
   {
40
0
   static Thread_Pool g_thread_pool(global_thread_pool_size());
41
0
   return g_thread_pool;
42
0
   }
43
44
Thread_Pool::Thread_Pool(std::optional<size_t> opt_pool_size)
45
0
   {
46
0
   m_shutdown = false;
47
48
0
   if(!opt_pool_size.has_value())
49
0
      return;
50
51
0
   size_t pool_size = opt_pool_size.value();
52
53
0
   if(pool_size == 0)
54
0
      {
55
0
      pool_size = OS::get_cpu_available();
56
57
      // Unclear if this can happen, but be defensive
58
0
      if(pool_size == 0)
59
0
         pool_size = 2;
60
61
      /*
62
      * For large machines don't create too many threads, unless
63
      * explicitly asked to by the caller.
64
      */
65
0
      if(pool_size > 16)
66
0
         pool_size = 16;
67
0
      }
68
69
0
   for(size_t i = 0; i != pool_size; ++i)
70
0
      {
71
0
      m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this));
72
0
      }
73
0
   }
74
75
void Thread_Pool::shutdown()
76
0
   {
77
0
      {
78
0
      std::unique_lock<std::mutex> lock(m_mutex);
79
80
0
      if(m_shutdown == true)
81
0
         return;
82
83
0
      m_shutdown = true;
84
85
0
      m_more_tasks.notify_all();
86
0
      }
87
88
0
   for(auto&& thread : m_workers)
89
0
      {
90
0
      thread.join();
91
0
      }
92
0
   m_workers.clear();
93
0
   }
94
95
void Thread_Pool::queue_thunk(std::function<void ()> fn)
96
0
   {
97
0
   std::unique_lock<std::mutex> lock(m_mutex);
98
99
0
   if(m_shutdown)
100
0
      throw Invalid_State("Cannot add work after thread pool has shut down");
101
102
0
   if(m_workers.empty())
103
0
      {
104
0
      return fn();
105
0
      }
106
107
0
   m_tasks.push_back(fn);
108
0
   m_more_tasks.notify_one();
109
0
   }
110
111
void Thread_Pool::worker_thread()
112
0
   {
113
0
   for(;;)
114
0
      {
115
0
      std::function<void()> task;
116
117
0
         {
118
0
         std::unique_lock<std::mutex> lock(m_mutex);
119
0
         m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); });
120
121
0
         if(m_tasks.empty())
122
0
            {
123
0
            if(m_shutdown)
124
0
               return;
125
0
            else
126
0
               continue;
127
0
            }
128
129
0
         task = m_tasks.front();
130
0
         m_tasks.pop_front();
131
0
         }
132
133
0
      task();
134
0
      }
135
0
   }
136
137
}