1 /*
2 * (C) 2019 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 //static
global_instance()15 Thread_Pool& Thread_Pool::global_instance()
16    {
17    static Thread_Pool g_thread_pool(OS::read_env_variable_sz("BOTAN_THREAD_POOL_SIZE"));
18    return g_thread_pool;
19    }
20 
Thread_Pool(size_t pool_size)21 Thread_Pool::Thread_Pool(size_t pool_size)
22    {
23    if(pool_size == 0)
24       {
25       pool_size = OS::get_cpu_available();
26 
27       /*
28       * For large machines don't create too many threads, unless
29       * explicitly asked to by the caller.
30       */
31       if(pool_size > 16)
32          pool_size = 16;
33       }
34 
35    if(pool_size <= 1)
36       pool_size = 2;
37 
38    m_shutdown = false;
39 
40    for(size_t i = 0; i != pool_size; ++i)
41       {
42       m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this));
43       }
44    }
45 
shutdown()46 void Thread_Pool::shutdown()
47    {
48       {
49       std::unique_lock<std::mutex> lock(m_mutex);
50 
51       if(m_shutdown == true)
52          return;
53 
54       m_shutdown = true;
55 
56       m_more_tasks.notify_all();
57       }
58 
59    for(auto&& thread : m_workers)
60       {
61       thread.join();
62       }
63    m_workers.clear();
64    }
65 
queue_thunk(std::function<void ()> fn)66 void Thread_Pool::queue_thunk(std::function<void ()> fn)
67    {
68    std::unique_lock<std::mutex> lock(m_mutex);
69 
70    if(m_shutdown)
71       throw Invalid_State("Cannot add work after thread pool has shut down");
72 
73    m_tasks.push_back(fn);
74    m_more_tasks.notify_one();
75    }
76 
worker_thread()77 void Thread_Pool::worker_thread()
78    {
79    for(;;)
80       {
81       std::function<void()> task;
82 
83          {
84          std::unique_lock<std::mutex> lock(m_mutex);
85          m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); });
86 
87          if(m_tasks.empty())
88             {
89             if(m_shutdown)
90                return;
91             else
92                continue;
93             }
94 
95          task = m_tasks.front();
96          m_tasks.pop_front();
97          }
98 
99       task();
100       }
101    }
102 
103 }
104