ThreadPool.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package ArmarX
19  * @author Mirko Waechter( mirko.waechter at kit dot edu)
20  * @date 2018
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 #include "ThreadPool.h"
26 
29 
30 // This was transitively included from some other STL header earlier,
31 // but not anymore since GCC 12. This change will make building boost
32 // asio fail now. See also https://github.com/doxygen/doxygen/issues/8909
33 // for a similar issue in Doxygen.
34 #include <utility>
35 
36 #include <boost/asio.hpp>
37 #include <boost/thread.hpp>
38 
39 
40 namespace armarx
41 {
43  {
44  Impl(std::size_t pool_size, bool queueTasks)
45  : work(io_service)
46  , available(pool_size)
48  { }
49 
50  boost::asio::io_service io_service;
51  boost::asio::io_service::work work;
52  boost::thread_group threads;
53  std::size_t available;
54  std::mutex mutex;
55  bool queueTasks;
56  };
57 
58  ThreadPool::ThreadPool(std::size_t pool_size, bool queueTasks)
59  : impl(new Impl(pool_size, queueTasks))
60  {
61  for (std::size_t i = 0; i < pool_size; ++i)
62  {
63  impl->threads.create_thread(boost::bind(&boost::asio::io_service::run,
64  &impl->io_service));
65  }
66  }
67 
69  {
70  // Force all threads to return from io_service::run().
71  impl->io_service.stop();
72 
73  // Suppress all exceptions.
74  try
75  {
76  impl->threads.join_all();
77  }
78  catch (const std::exception&) {}
79  }
80 
81  ThreadPool::Handle ThreadPool::runTask(std::function<void ()> task)
82  {
83  std::unique_lock lock(impl->mutex);
84 
85  // If no threads are available, then return.
86  if (!impl->queueTasks && impl->available == 0)
87  {
88  return {};
89  }
90 
91  // Decrement count, indicating thread is no longer available.
92  if (!impl->queueTasks)
93  {
94  --impl->available;
95  }
96  auto promise = std::make_shared<std::promise<void>>();
97  // Post a wrapped task into the queue.
98  impl->io_service.post([this, task, promise]
99  {
100  wrap_task(task);
101  promise->set_value();
102  });
103  return Handle {promise->get_future()};
104  }
105 
107  {
108  std::unique_lock lock(impl->mutex);
109  if (!impl->queueTasks)
110  {
111  return impl->available;
112  }
113  else
114  {
115  return -1;
116  }
117  }
118 
119  void ThreadPool::wrap_task(std::function<void ()> task)
120  {
121  // Run the user supplied task.
122  try
123  {
124  task();
125  }
126  // Suppress all exceptions.
127  catch (const std::exception&)
128  {
129  std::unique_lock lock(impl->mutex);
130  // Task has ended, so increment count of available threads.
131  if (!impl->queueTasks)
132  {
133  ++impl->available;
134  }
136  }
137 
138  // Task has finished, so increment count of available threads.
139  std::unique_lock lock(impl->mutex);
140  if (!impl->queueTasks)
141  {
142  ++impl->available;
143  }
144  }
145 
146 
148  mutex(new std::mutex)
149  {
150 
151  }
152 
153  ThreadPool::Handle::Handle(std::shared_future<void> functionFinished) :
154  mutex(new std::mutex),
155  functionFinished(functionFinished)
156  {
157 
158  }
159 
160  ThreadPool::Handle::~Handle() noexcept(false)
161  {
162  if (!joined && isValid() && !detached)
163  {
164  throw LocalException() << "You did not join the thread pool handle before the handle was deleted!";
165  }
166  }
167 
169  {
170  return functionFinished.valid();
171  }
172 
174  {
176  std::lock_guard<decltype(*mutex)> lock(*mutex);
177 
178  if (!isValid())
179  {
180  return;
181  }
182  if (detached)
183  {
184  throw LocalException() << "You cannot join a detached thread!";
185  }
186  functionFinished.get();
187  joined = true;
188  }
189 
191  {
193  std::lock_guard<decltype(*mutex)> lock(*mutex);
194 
195  if (!isValid())
196  {
197  return;
198  }
199  if (joined)
200  {
201  throw LocalException() << "You cannot detach a joined thread!";
202  }
203  detached = true;
204  }
205 
206  const std::shared_future<void>& ThreadPool::Handle::getFuture() const
207  {
208  return functionFinished;
209  }
210 
212  {
213  return joined;
214  }
215 
217  {
218  return detached;
219  }
220 
221 } // namespace armarx
armarx::ThreadPool::ThreadPool
ThreadPool(std::size_t pool_size, bool queueTasks=false)
Constructor.
Definition: ThreadPool.cpp:58
armarx::ThreadPool::Handle::Handle
Handle()
Definition: ThreadPool.cpp:147
armarx::ThreadPool::Handle
Definition: ThreadPool.h:52
armarx::ThreadPool::getAvailableTaskCount
int getAvailableTaskCount() const
If queing is disabled, returns the number of available threads.
Definition: ThreadPool.cpp:106
armarx::ThreadPool::Handle::isJoined
bool isJoined() const
Definition: ThreadPool.cpp:211
armarx::ThreadPool::Impl::queueTasks
bool queueTasks
Definition: ThreadPool.cpp:55
armarx::ThreadPool::Handle::isDetached
bool isDetached() const
Definition: ThreadPool.cpp:216
armarx::ThreadPool::Impl::Impl
Impl(std::size_t pool_size, bool queueTasks)
Definition: ThreadPool.cpp:44
armarx::ThreadPool::Impl
Definition: ThreadPool.cpp:42
ThreadPool.h
armarx::ThreadPool::Impl::io_service
boost::asio::io_service io_service
Definition: ThreadPool.cpp:50
armarx::ThreadPool::Handle::join
void join()
Definition: ThreadPool.cpp:173
armarx::ThreadPool::~ThreadPool
~ThreadPool()
Destructor.
Definition: ThreadPool.cpp:68
armarx::ThreadPool::Handle::~Handle
~Handle() noexcept(false)
Definition: ThreadPool.cpp:160
armarx::ThreadPool::Handle::detach
void detach()
Definition: ThreadPool.cpp:190
armarx::ThreadPool::Handle::getFuture
const std::shared_future< void > & getFuture() const
Definition: ThreadPool.cpp:206
ExpressionException.h
armarx::ThreadPool::Impl::available
std::size_t available
Definition: ThreadPool.cpp:53
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::ThreadPool::Impl::mutex
std::mutex mutex
Definition: ThreadPool.cpp:54
armarx::ThreadPool::Impl::work
boost::asio::io_service::work work
Definition: ThreadPool.cpp:51
std
Definition: Application.h:66
armarx::ThreadPool::Impl::threads
boost::thread_group threads
Definition: ThreadPool.cpp:52
armarx::ThreadPool::Handle::isValid
bool isValid() const
Definition: ThreadPool.cpp:168
armarx::ThreadPool::runTask
Handle runTask(std::function< void()> task)
Adds a task to the thread pool if a thread is currently available.
Definition: ThreadPool.cpp:81
armarx::handleExceptions
void handleExceptions()
Definition: Exception.cpp:141
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
Exception.h