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  {
130  }
131 
132  // Task has finished, so increment count of available threads.
133  std::unique_lock lock(impl->mutex);
134  if (!impl->queueTasks)
135  {
136  ++impl->available;
137  }
138  }
139 
140 
142  mutex(new std::mutex)
143  {
144 
145  }
146 
147  ThreadPool::Handle::Handle(std::shared_future<void> functionFinished) :
148  mutex(new std::mutex),
149  functionFinished(functionFinished)
150  {
151 
152  }
153 
154  ThreadPool::Handle::~Handle() noexcept(false)
155  {
156  if (!joined && isValid() && !detached)
157  {
158  throw LocalException() << "You did not join the thread pool handle before the handle was deleted!";
159  }
160  }
161 
163  {
164  return functionFinished.valid();
165  }
166 
168  {
170  std::lock_guard<decltype(*mutex)> lock(*mutex);
171 
172  if (!isValid())
173  {
174  return;
175  }
176  if (detached)
177  {
178  throw LocalException() << "You cannot join a detached thread!";
179  }
180  functionFinished.get();
181  joined = true;
182  }
183 
185  {
187  std::lock_guard<decltype(*mutex)> lock(*mutex);
188 
189  if (!isValid())
190  {
191  return;
192  }
193  if (joined)
194  {
195  throw LocalException() << "You cannot detach a joined thread!";
196  }
197  detached = true;
198  }
199 
200  const std::shared_future<void>& ThreadPool::Handle::getFuture() const
201  {
202  return functionFinished;
203  }
204 
206  {
207  return joined;
208  }
209 
211  {
212  return detached;
213  }
214 
215 } // 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:141
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:205
armarx::ThreadPool::Impl::queueTasks
bool queueTasks
Definition: ThreadPool.cpp:55
armarx::ThreadPool::Handle::isDetached
bool isDetached() const
Definition: ThreadPool.cpp:210
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:167
armarx::ThreadPool::~ThreadPool
~ThreadPool()
Destructor.
Definition: ThreadPool.cpp:68
armarx::ThreadPool::Handle::~Handle
~Handle() noexcept(false)
Definition: ThreadPool.cpp:154
armarx::ThreadPool::Handle::detach
void detach()
Definition: ThreadPool.cpp:184
armarx::ThreadPool::Handle::getFuture
const std::shared_future< void > & getFuture() const
Definition: ThreadPool.cpp:200
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:162
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