aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/utils/thread_utils/thread_pool.cpp
blob: 4ccefe8dcc5c874b996b39c91a6b579b8f572ee6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
* (C) 2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/thread_pool.h>
#include <botan/internal/os_utils.h>
#include <botan/exceptn.h>
#include <thread>

namespace Botan {

//static
Thread_Pool& Thread_Pool::global_instance()
   {
   static Thread_Pool g_thread_pool(OS::read_env_variable_sz("BOTAN_THREAD_POOL_SIZE"));
   return g_thread_pool;
   }

Thread_Pool::Thread_Pool(size_t pool_size)
   {
   if(pool_size == 0)
      {
      pool_size = std::thread::hardware_concurrency();

      /*
      * For large machines don't create too many threads, unless
      * explicitly asked to by the caller.
      */
      if(pool_size > 16)
         pool_size = 16;
      }

   if(pool_size <= 1)
      pool_size = 2;

   m_shutdown = false;

   for(size_t i = 0; i != pool_size; ++i)
      {
      m_workers.push_back(std::thread(&Thread_Pool::worker_thread, this));
      }
   }

void Thread_Pool::shutdown()
   {
      {
      std::unique_lock<std::mutex> lock(m_mutex);

      if(m_shutdown == true)
         return;

      m_shutdown = true;

      m_more_tasks.notify_all();
      }

   for(auto&& thread : m_workers)
      {
      thread.join();
      }
   m_workers.clear();
   }

void Thread_Pool::queue_thunk(std::function<void ()> fn)
   {
   std::unique_lock<std::mutex> lock(m_mutex);

   if(m_shutdown)
      throw Invalid_State("Cannot add work after thread pool has shut down");

   m_tasks.push_back(fn);
   m_more_tasks.notify_one();
   }

void Thread_Pool::worker_thread()
   {
   for(;;)
      {
      std::function<void()> task;

         {
         std::unique_lock<std::mutex> lock(m_mutex);
         m_more_tasks.wait(lock, [this]{ return m_shutdown || !m_tasks.empty(); });

         if(m_tasks.empty())
            {
            if(m_shutdown)
               return;
            else
               continue;
            }

         task = m_tasks.front();
         m_tasks.pop_front();
         }

      task();
      }
   }

}