Skip to content

Commit 32f9a3c

Browse files
authored
ThreadPool::Config (#23)
* ThreadPool::Config + miscellaneous fixes - ThreadPool::Config - reworked minimal example to use explicit thread-pool - fixed issue with get_default_threadpool() / TaskRunManager deadlock - Support for setting thread priority - default get/set thread-pool options - TaskManager can avoid finalization * Bumped version to 2.3.0 * Fixed comment * Threading::GetNumberOfPhysicalCpus * Return type casting * Tweak to examples/minimal * Fix for minimal + TBB * Memory leak fix * Fixed data race * Improved coverage * Fix to reinitialize threadpool + TBB * UserTaskQueue use-after-free fix * clang-tidy
1 parent c898062 commit 32f9a3c

16 files changed

Lines changed: 661 additions & 207 deletions

.clang-tidy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ modernize-*,\
1919
-modernize-use-auto,\
2020
-modernize-concat-nested-namespaces,\
2121
-modernize-use-nodiscard,\
22+
-modernize-make-unique,\
2223
performance-*,\
2324
readability-*,\
2425
-readability-function-size,\

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ endif()
1717
# Check if project is being used directly or via add_subdirectory
1818
set(PTL_MASTER_PROJECT ON)
1919
if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
20-
set(PTL_MASTER_PROJECT OFF)
20+
set(PTL_MASTER_PROJECT OFF)
21+
else()
22+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
2123
endif()
2224

2325
# Version

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.2.1
1+
2.3.0

examples/minimal/minimal.cc

Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@
2020
/// \brief Example showing the usage of tasking
2121

2222
#include "PTL/PTL.hh"
23+
#include "PTL/ThreadPool.hh"
24+
#include "PTL/Threading.hh"
2325

2426
#include <chrono>
2527
#include <condition_variable>
2628
#include <iostream>
29+
#include <memory>
2730
#include <mutex>
2831
#include <random>
32+
#include <stdexcept>
2933
#include <thread>
3034
#include <vector>
3135

@@ -34,7 +38,7 @@ using lock_t = std::unique_lock<mutex_t>;
3438

3539
using namespace PTL;
3640

37-
static std::mt19937 rng;
41+
static thread_local std::mt19937 rng{};
3842

3943
//============================================================================//
4044

@@ -75,7 +79,6 @@ Tp
7579
random_entry(const std::vector<Tp>& v)
7680
{
7781
std::uniform_int_distribution<std::mt19937::result_type> dist(0, v.size() - 1);
78-
AutoLock lk{ TypeMutex<decltype(rng)>() };
7982
return v.at(dist(rng));
8083
}
8184

@@ -85,7 +88,7 @@ int
8588
main(int argc, char** argv)
8689
{
8790
ConsumeParameters(argc, argv);
88-
rng.seed(std::random_device()());
91+
rng.seed(std::random_device{}());
8992
Threading::SetThreadId(0);
9093
Backtrace::Enable();
9194

@@ -97,20 +100,74 @@ main(int argc, char** argv)
97100
if(nthreads == 0)
98101
nthreads = 1;
99102

100-
std::cout << "[" << argv[0] << "]> "
103+
std::cout << "[ptl-minimal]> "
101104
<< "Number of threads: " << nthreads << std::endl;
102105
Timer total_timer;
103106
total_timer.Start();
104107

105108
// Construct the default run manager
106-
bool use_tbb = GetEnv("PTL_USE_TBB", false);
107-
auto run_manager = TaskRunManager(use_tbb);
108-
run_manager.Initialize(nthreads);
109+
bool _use_tbb = GetEnv("PTL_USE_TBB", false);
110+
bool _pin = GetEnv("PTL_PIN_THREADS", true);
111+
112+
std::atomic<unsigned> ninit{ 0 };
113+
std::atomic<unsigned> nfini{ 0 };
114+
auto _init = [&ninit]() {
115+
rng.seed(std::random_device{}());
116+
++ninit;
117+
AutoLock _lk{ TypeMutex<decltype(std::cout)>() };
118+
printf("[ptl-minimal]> Thread %2i started\n", Threading::GetThreadId());
119+
};
120+
auto _fini = [&nfini]() {
121+
++nfini;
122+
AutoLock _lk{ TypeMutex<decltype(std::cout)>() };
123+
printf("[ptl-minimal]> Thread %2i finished\n", Threading::GetThreadId());
124+
};
125+
126+
PTL::ThreadPool::Config _config{};
127+
_config.use_tbb = _use_tbb;
128+
_config.use_affinity = _pin;
129+
_config.verbose = 3;
130+
_config.pool_size = nthreads;
131+
_config.initializer = _init;
132+
_config.finalizer = _fini;
133+
_config.set_affinity = [](intmax_t i) {
134+
auto _orig = ThreadPool::affinity_functor()(i);
135+
static intmax_t idx = 0;
136+
static intmax_t ncores = Threading::GetNumberOfCores();
137+
static intmax_t ncpus = Threading::GetNumberOfPhysicalCpus();
138+
static intmax_t nincr = std::max<intmax_t>(ncores / ncpus, 1);
139+
auto _idx = idx + nincr;
140+
idx += nincr;
141+
if(_idx % ncores == 0)
142+
idx++;
143+
auto _v = (_idx - nincr) % ncores;
144+
AutoLock _lk{ TypeMutex<decltype(std::cout)>() };
145+
printf("[ptl-minimal]> Thread %2i was pinned to CPU %2i instead of %2i...\n",
146+
(int) i, (int) _v, (int) _orig);
147+
return _v;
148+
};
149+
150+
auto tp = std::unique_ptr<PTL::ThreadPool>{ new PTL::ThreadPool{ _config } };
151+
152+
if(!tp->is_initialized())
153+
throw std::runtime_error("ThreadPool is not initialized");
154+
if(!_use_tbb && !tp->is_alive())
155+
throw std::runtime_error("ThreadPool is not alive");
156+
if(_use_tbb && tp->is_alive())
157+
throw std::runtime_error("ThreadPool is alive and shouldn't be");
158+
if(_use_tbb && !tp->is_tbb_threadpool())
159+
throw std::runtime_error("ThreadPool is not a TBB threadpool (should be)");
160+
if(!_use_tbb && tp->is_tbb_threadpool())
161+
throw std::runtime_error("ThreadPool is a TBB threadpool (should not be)");
162+
if(!tp->is_main())
163+
throw std::runtime_error("ThreadPool does not think it is on main thread");
109164

110165
// the TaskManager is a utility that wraps the function calls into tasks for the
111-
// ThreadPool
112-
TaskManager* task_manager = run_manager.GetTaskManager();
113-
auto* tp = task_manager->thread_pool();
166+
// ThreadPool and helps manage the lifetime of the threadpool (it will stop the
167+
// threads in the thread pool when it's destructor is called unless false
168+
// is passed as second parameter)
169+
TaskManager task_manager{ tp.get(), false };
170+
114171
std::set<std::thread::id> tids{};
115172

116173
//------------------------------------------------------------------------//
@@ -123,7 +180,8 @@ main(int argc, char** argv)
123180
tp->execute_on_all_threads([&tids, &_all_exec]() {
124181
++_all_exec;
125182
std::stringstream ss;
126-
ss << "thread " << std::setw(4) << PTL::Threading::GetThreadId() << " executed\n";
183+
ss << "[ptl-minimal]> Thread " << std::setw(2) << PTL::Threading::GetThreadId()
184+
<< " executed\n";
127185
AutoLock lk{ TypeMutex<decltype(std::cout)>() };
128186
std::cout << ss.str();
129187
tids.insert(std::this_thread::get_id());
@@ -136,7 +194,8 @@ main(int argc, char** argv)
136194
std::to_string(_all_exec.load()) + " vs. " + std::to_string(_size));
137195
else
138196
{
139-
printf("Successful execution on every thread: %i\n", (int) _all_exec);
197+
printf("[ptl-minimal]> Successful execution on every thread: %i\n",
198+
(int) _all_exec);
140199
}
141200

142201
//------------------------------------------------------------------------//
@@ -155,7 +214,8 @@ main(int argc, char** argv)
155214
tp->execute_on_specific_threads(tids, [&_specific_exec]() {
156215
++_specific_exec;
157216
std::stringstream ss;
158-
ss << "thread " << std::setw(4) << PTL::Threading::GetThreadId() << " executed [specific]\n";
217+
ss << "[ptl-minimal]> Thread " << std::setw(2) << PTL::Threading::GetThreadId()
218+
<< " executed [specific]\n";
159219
AutoLock lk{ TypeMutex<decltype(std::cout)>() };
160220
std::cout << ss.str();
161221
});
@@ -166,7 +226,8 @@ main(int argc, char** argv)
166226
std::to_string(_specific_exec.load()) + " vs. " + std::to_string(_target_sz));
167227
else
168228
{
169-
printf("Successful execution on subset of threads: %i\n", (int) _specific_exec);
229+
printf("[ptl-minimal]> Successful execution on subset of threads: %i\n",
230+
(int) _specific_exec);
170231
}
171232

172233
//------------------------------------------------------------------------//
@@ -175,41 +236,40 @@ main(int argc, char** argv)
175236
// //
176237
//------------------------------------------------------------------------//
177238
{
178-
long nfib = std::max<long>(GetEnv<long>("FIBONACCI", 30), 30);
179-
long nloop = 100;
180-
long ndiv = 4;
181-
long npart = nloop / ndiv;
239+
long nfib = std::max<long>(GetEnv<long>("FIBONACCI", 30), 30);
240+
long nloop = 100;
241+
long ndiv = 4;
242+
long npart = nloop / ndiv;
182243
long expected = (fibonacci(nfib + 0) * npart) + (fibonacci(nfib + 1) * npart) +
183244
(fibonacci(nfib + 2) * npart) + (fibonacci(nfib + 3) * npart);
184245
auto join = [](long& lhs, long rhs) {
185246
std::stringstream ss;
186-
ss << "thread " << std::setw(4) << PTL::Threading::GetThreadId() << " adding "
187-
<< rhs << " to " << lhs << std::endl;
247+
ss << "[ptl-minimal]> Thread " << std::setw(2)
248+
<< PTL::Threading::GetThreadId() << " adding " << rhs << " to " << lhs
249+
<< std::endl;
188250
{
189251
AutoLock lk{ TypeMutex<decltype(std::cout)>() };
190252
std::cout << ss.str();
191253
}
192254
return lhs += rhs;
193255
};
194256

195-
auto entry = [](uint64_t n) {
257+
auto entry = [](uint64_t n) {
196258
std::vector<double> v(n * 100, 0);
197259
for(auto& itr : v)
198-
{
199-
AutoLock lk{ TypeMutex<decltype(rng)>() };
200260
itr = std::generate_canonical<double, 12>(rng);
201-
}
202-
auto e = random_entry(v);
261+
auto e = random_entry(v);
203262
std::stringstream ss;
204-
ss << "[" << n << "]> random entry from thread " << std::setw(4)
205-
<< PTL::Threading::GetThreadId() << " was : " << std::setw(8)
206-
<< std::setprecision(6) << std::fixed << e << std::endl;
263+
ss << "[ptl-minimal][" << std::setw(4) << n << "]> Random entry from thread "
264+
<< std::setw(2) << PTL::Threading::GetThreadId()
265+
<< " was : " << std::setw(8) << std::setprecision(6) << std::fixed << e
266+
<< std::endl;
207267
AutoLock lk{ TypeMutex<decltype(std::cout)>() };
208268
std::cout << ss.str();
209269
};
210270

211-
TaskGroup<long> tgf(join);
212-
TaskGroup<void> tgv;
271+
TaskGroup<long> tgf{ join, tp.get() }; // uses existing thread-pool
272+
TaskGroup<void> tgv{ tp.get() }; // uses existing thread-pool
213273
for(long i = 0; i < nloop; ++i)
214274
{
215275
tgf.exec(fibonacci, nfib + (i % ndiv));
@@ -220,7 +280,8 @@ main(int argc, char** argv)
220280

221281
auto ret = tgf.join();
222282
tgv.join();
223-
std::cout << "fibonacci(" << nfib << ") * " << nloop << " = " << ret << std::endl;
283+
std::cout << "[ptl-minimal]> fibonacci(" << nfib << ") * " << nloop << " = "
284+
<< ret << std::endl;
224285
std::cout << std::endl;
225286
if(expected != ret)
226287
{
@@ -245,7 +306,7 @@ main(int argc, char** argv)
245306
_at.Start();
246307
for(decltype(nthreads) i = 0; i < nthreads; ++i)
247308
{
248-
auto fib_async = task_manager->async<int64_t>(fibonacci, nfib);
309+
auto fib_async = task_manager.async<int64_t>(fibonacci, nfib);
249310
_futures.emplace_back(fib_async->get_future());
250311
_asyncs.emplace_back(fib_async);
251312
}
@@ -258,10 +319,10 @@ main(int argc, char** argv)
258319
_at.Stop();
259320
for(decltype(nthreads) i = 0; i < nthreads; ++i)
260321
{
261-
std::cout << "[async test][" << i << "] fibonacci(" << nfib
322+
std::cout << "[ptl-minimal][async test][" << i << "] fibonacci(" << nfib
262323
<< ") = " << _values[i] << std::endl;
263324
}
264-
std::cout << "[async test] " << _at << std::endl;
325+
std::cout << "[ptl-minimal][async test] " << _at << std::endl;
265326
std::cout << std::endl;
266327
}
267328

@@ -273,8 +334,18 @@ main(int argc, char** argv)
273334

274335
// print the time for the calculation
275336
total_timer.Stop();
276-
std::cout << "Total time: \t" << total_timer << std::endl;
337+
std::cout << "[ptl-minimal]> Total time: \t" << total_timer << std::endl;
277338

339+
tp->resize(nthreads);
278340
tp->destroy_threadpool();
279-
task_manager->finalize();
341+
tp->resize(0);
342+
343+
std::cout << "[ptl-minimal]> Number of threads: " << nthreads << "\n";
344+
std::cout << "[ptl-minimal]> Number of thread initialization: " << ninit.load()
345+
<< "\n";
346+
std::cout << "[ptl-minimal]> Number of thread finalizations : " << nfini.load()
347+
<< "\n";
348+
349+
tp.reset();
350+
return (2 * nthreads) - ninit - nfini;
280351
}

source/PTL/Backtrace.hh

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,6 @@
5656
#include "Threading.hh"
5757
#include "Types.hh"
5858

59-
#if defined(__APPLE__) || defined(__MACH__)
60-
# if !defined(PTL_MACOS)
61-
# define PTL_MACOS
62-
# endif
63-
# if !defined(PTL_UNIX)
64-
# define PTL_UNIX
65-
# endif
66-
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
67-
# if !defined(PTL_LINUX)
68-
# define PTL_LINUX
69-
# endif
70-
# if !defined(PTL_UNIX)
71-
# define PTL_UNIX
72-
# endif
73-
#elif defined(__unix__) || defined(__unix) || defined(unix)
74-
# if !defined(PTL_UNIX)
75-
# define PTL_UNIX
76-
# endif
77-
#endif
78-
7959
#if defined(PTL_UNIX)
8060
# include <cxxabi.h>
8161
# include <execinfo.h>

0 commit comments

Comments
 (0)