libMesh
Loading...
Searching...
No Matches
threads_pthread.h
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18#ifndef LIBMESH_THREADS_PTHREAD_H
19#define LIBMESH_THREADS_PTHREAD_H
20
21// Do not try to #include this header directly, it is designed to be
22// #included directly by threads.h
23#ifndef LIBMESH_SQUASH_HEADER_WARNING
24# warning "This file is designed to be included through libmesh/threads.h"
25#else
26
27#ifdef LIBMESH_HAVE_PTHREAD
28
29// C++ includes
30#ifdef LIBMESH_HAVE_CXX11_THREAD
31# include <thread>
32#endif
33
34#include <pthread.h>
35#include <algorithm>
36#include <vector>
37#include <memory> // std::unique_ptr, std::make_unique
38
39#ifdef __APPLE__
40# ifdef __MAC_10_12
41# include <os/lock.h>
42#else
43# include <libkern/OSAtomic.h>
44# endif
45#endif
46
47// Thread-Local-Storage macros
48#ifdef LIBMESH_HAVE_CXX11_THREAD
49# define LIBMESH_TLS_TYPE(type) thread_local type
50# define LIBMESH_TLS_REF(value) (value)
51#else // Maybe support gcc __thread eventually?
52# define LIBMESH_TLS_TYPE(type) type
53# define LIBMESH_TLS_REF(value) (value)
54#endif
55
56namespace libMesh
57{
58
59namespace Threads
60{
61
62
63#ifdef LIBMESH_HAVE_CXX11_THREAD
67typedef std::thread Thread;
68
69#else
70
74typedef NonConcurrentThread Thread;
75
76#endif // LIBMESH_HAVE_CXX11_THREAD
77
78
83#ifdef __APPLE__
84#ifdef __MAC_10_12
85class spin_mutex
86{
87public:
88 spin_mutex() { ulock = OS_UNFAIR_LOCK_INIT; }
89 ~spin_mutex() = default;
90
91 void lock () { os_unfair_lock_lock(&ulock); }
92 void unlock () { os_unfair_lock_unlock(&ulock); }
93
94 class scoped_lock
95 {
96 public:
97 scoped_lock () : smutex(nullptr) {}
98 explicit scoped_lock ( spin_mutex & in_smutex ) : smutex(&in_smutex) { smutex->lock(); }
99
101
102 void acquire ( spin_mutex & in_smutex ) { smutex = &in_smutex; smutex->lock(); }
103 void release () { if (smutex) smutex->unlock(); smutex = nullptr; }
104
105 private:
107 };
108
109private:
110 os_unfair_lock ulock;
111};
112#else
113class spin_mutex
114{
115public:
116 spin_mutex() : slock(0) {} // The convention is that the lock being zero is _unlocked_
117 ~spin_mutex() = default;
118
119 void lock () { OSSpinLockLock(&slock); }
120 void unlock () { OSSpinLockUnlock(&slock); }
121
122 class scoped_lock
123 {
124 public:
125 scoped_lock () : smutex(nullptr) {}
126 explicit scoped_lock ( spin_mutex & in_smutex ) : smutex(&in_smutex) { smutex->lock(); }
127
129
130 void acquire ( spin_mutex & in_smutex ) { smutex = &in_smutex; smutex->lock(); }
131 void release () { if (smutex) smutex->unlock(); smutex = nullptr; }
132
133 private:
134 spin_mutex * smutex;
135 };
136
137private:
138 OSSpinLock slock;
139};
140#endif
141#else
142class spin_mutex
143{
144public:
145 // Might want to use PTHREAD_MUTEX_ADAPTIVE_NP on Linux, but it's not available on OSX.
146 spin_mutex() { pthread_spin_init(&slock, PTHREAD_PROCESS_PRIVATE); }
147 ~spin_mutex() { pthread_spin_destroy(&slock); }
148
149 void lock () { pthread_spin_lock(&slock); }
150 void unlock () { pthread_spin_unlock(&slock); }
151
152 class scoped_lock
153 {
154 public:
155 scoped_lock () : smutex(nullptr) {}
156 explicit scoped_lock ( spin_mutex & in_smutex ) : smutex(&in_smutex) { smutex->lock(); }
157
159
160 void acquire ( spin_mutex & in_smutex ) { smutex = &in_smutex; smutex->lock(); }
161 void release () { if (smutex) smutex->unlock(); smutex = nullptr; }
162
163 private:
164 spin_mutex * smutex;
165 };
166
167private:
168 pthread_spinlock_t slock;
169};
170#endif // __APPLE__
171
172
173
178class recursive_mutex
179{
180public:
181 // Might want to use PTHREAD_MUTEX_ADAPTIVE_NP on Linux, but it's not available on OSX.
183 {
184 pthread_mutexattr_init(&attr);
185 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
186 pthread_mutex_init(&mutex, &attr);
187 }
188 ~recursive_mutex() { pthread_mutex_destroy(&mutex); }
189
190 void lock () { pthread_mutex_lock(&mutex); }
191 void unlock () { pthread_mutex_unlock(&mutex); }
192
193 class scoped_lock
194 {
195 public:
196 scoped_lock () : rmutex(nullptr) {}
197 explicit scoped_lock ( recursive_mutex & in_rmutex ) : rmutex(&in_rmutex) { rmutex->lock(); }
198
200
201 void acquire ( recursive_mutex & in_rmutex ) { rmutex = &in_rmutex; rmutex->lock(); }
202 void release () { if (rmutex) rmutex->unlock(); rmutex = nullptr; }
203
204 private:
206 };
207
208private:
209 pthread_mutex_t mutex;
210 pthread_mutexattr_t attr;
211};
212
213template <typename Range>
214unsigned int num_pthreads(const Range & range,
215 unsigned int requested = libMesh::n_threads())
216{
217 std::size_t mn = std::min((std::size_t)requested, range.size()/range.grainsize());
218 return mn > 0 ? cast_int<unsigned int>(mn) : 1;
219}
220
221template <typename Range, typename Body>
223{
224public:
225 Range * range;
226 Body * body;
227};
228
229template <typename Range, typename Body>
230void * run_body(void * args)
231{
232 RangeBody<Range, Body> * range_body = (RangeBody<Range, Body> *)args;
233
234 Body & body = *range_body->body;
235 Range & range = *range_body->range;
236
237 body(range);
238
239 return nullptr;
240}
241
245class task_scheduler_init
246{
247public:
248 static const int automatic = -1;
249 explicit task_scheduler_init (int = automatic) {}
250 void initialize (int = automatic) {}
251 void terminate () {}
252};
253
254//-------------------------------------------------------------------
259class split {};
260
261
262
263
264//-------------------------------------------------------------------
269template <typename Range, typename Body>
270inline
271void parallel_for (const Range & range, const Body & body,
272 unsigned int n_threads = libMesh::n_threads())
273{
274 libmesh_error_msg_if(n_threads > libMesh::n_threads(),
275 "Requested n_threads (" << n_threads << ") exceeds the "
276 "global thread count (" << libMesh::n_threads() << ").");
278
279 unsigned int actual_threads = num_pthreads(range, n_threads);
280
281 // If we're running in serial - just run!
282 if (actual_threads == 1)
283 {
284 body(range);
285 return;
286 }
287
288 Threads::RAIIAcquire<int> set_active_threads(Threads::active_threads, actual_threads);
289
290 DisablePerfLogInScope disable_perf;
291
292 std::vector<std::unique_ptr<Range>> ranges(actual_threads);
293 std::vector<RangeBody<const Range, const Body>> range_bodies(actual_threads);
294 std::vector<pthread_t> threads(actual_threads);
295
296 // Create the ranges for each thread
297 std::size_t range_size = range.size() / actual_threads;
298
299 typename Range::const_iterator current_beginning = range.begin();
300
301 for (unsigned int i=0; i<actual_threads; i++)
302 {
303 std::size_t this_range_size = range_size;
304
305 if (i+1 == actual_threads)
306 this_range_size += range.size() % actual_threads; // Give the last one the remaining work to do
307
308 ranges[i] = std::make_unique<Range>(range, current_beginning, current_beginning + this_range_size);
309
310 current_beginning = current_beginning + this_range_size;
311 }
312
313 // Create the RangeBody arguments
314 for (unsigned int i=0; i<actual_threads; i++)
315 {
316 range_bodies[i].range = ranges[i].get();
317 range_bodies[i].body = &body;
318 }
319
320 // Create the threads. It may seem redundant to wrap a pragma in
321 // #ifdefs... but GCC warns about an "unknown pragma" if it
322 // encounters this line of code when -fopenmp is not passed to the
323 // compiler.
324#ifdef LIBMESH_HAVE_OPENMP
325// Cap the OpenMP team to the actual number of threads this dispatch uses. Without this clause the
326// team defaults to the process-wide libMesh::n_threads(), so omp_get_thread_num()
327// could exceed a reduced per-application thread count and index past
328// per-thread storage sized to that count.
329#pragma omp parallel for schedule (static) num_threads(actual_threads)
330#endif
331 for (int i=0; i<static_cast<int>(actual_threads); i++)
332 {
333#if !LIBMESH_HAVE_OPENMP
334 pthread_create(&threads[i], nullptr, &run_body<Range, Body>, (void *)&range_bodies[i]);
335#else
336 run_body<Range, Body>((void *)&range_bodies[i]);
337#endif
338 }
339
340#if !LIBMESH_HAVE_OPENMP
341 // Wait for them to finish
342
343 // The use of 'int' instead of unsigned for the iteration variable
344 // is deliberate here. This is an OpenMP loop, and some older
345 // compilers warn when you don't use int for the loop index. The
346 // reason has to do with signed vs. unsigned integer overflow
347 // behavior and optimization.
348 // http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
349 for (int i=0; i<static_cast<int>(actual_threads); i++)
350 pthread_join(threads[i], nullptr);
351#endif
352}
353
358template <typename Range, typename Body, typename Partitioner>
359inline
360void parallel_for (const Range & range, const Body & body, const Partitioner &,
361 unsigned int n_threads = libMesh::n_threads())
362{
363 parallel_for (range, body, n_threads);
364}
365
370template <typename Range, typename Body>
371inline
372void parallel_reduce (const Range & range, Body & body,
373 unsigned int n_threads = libMesh::n_threads())
374{
375 libmesh_error_msg_if(n_threads > libMesh::n_threads(),
376 "Requested n_threads (" << n_threads << ") exceeds the "
377 "global thread count (" << libMesh::n_threads() << ").");
379
380 unsigned int actual_threads = num_pthreads(range, n_threads);
381
382 // If we're running in serial - just run!
383 if (actual_threads == 1)
384 {
385 body(range);
386 return;
387 }
388
389 Threads::RAIIAcquire<int> set_active_threads(Threads::active_threads, actual_threads);
390
391 DisablePerfLogInScope disable_perf;
392
393 std::vector<std::unique_ptr<Range>> ranges(actual_threads);
394 std::vector<std::unique_ptr<Body>> managed_bodies(actual_threads); // bodies we are responsible for
395 std::vector<Body *> bodies(actual_threads); // dumb pointers to managed_bodies
396 std::vector<RangeBody<Range, Body>> range_bodies(actual_threads);
397
398 // Create actual_threads-1 copies of "body". We manage the lifetime of
399 // these copies with std::unique_ptrs.
400 for (unsigned int i=1; i<actual_threads; i++)
401 managed_bodies[i] = std::make_unique<Body>(body, Threads::split());
402
403 // Set up the "bodies" vector. Use the passed in body for the first
404 // one, point to managed_bodies entries for the others.
405 bodies[0] = &body;
406 for (unsigned int i=1; i<actual_threads; i++)
407 bodies[i] = managed_bodies[i].get();
408
409 // Create the ranges for each thread
410 std::size_t range_size = range.size() / actual_threads;
411
412 typename Range::const_iterator current_beginning = range.begin();
413
414 for (unsigned int i=0; i<actual_threads; i++)
415 {
416 std::size_t this_range_size = range_size;
417
418 if (i+1 == actual_threads)
419 this_range_size += range.size() % actual_threads; // Give the last one the remaining work to do
420
421 ranges[i] = std::make_unique<Range>(range, current_beginning, current_beginning + this_range_size);
422
423 current_beginning = current_beginning + this_range_size;
424 }
425
426 // Create the RangeBody arguments
427 for (unsigned int i=0; i<actual_threads; i++)
428 {
429 range_bodies[i].range = ranges[i].get();
430 range_bodies[i].body = bodies[i];
431 }
432
433 // Create the threads
434 std::vector<pthread_t> threads(actual_threads);
435
436 // It may seem redundant to wrap a pragma in #ifdefs... but GCC
437 // warns about an "unknown pragma" if it encounters this line of
438 // code when -fopenmp is not passed to the compiler.
439#ifdef LIBMESH_HAVE_OPENMP
440// Cap the OpenMP team to the actual number of threads this dispatch uses. Without this clause the
441// team defaults to the process-wide libMesh::n_threads(), so omp_get_thread_num()
442// could exceed a reduced per-application thread count and index past
443// per-thread storage sized to that count.
444#pragma omp parallel for schedule (static) num_threads(actual_threads)
445#endif
446 // The use of 'int' instead of unsigned for the iteration variable
447 // is deliberate here. This is an OpenMP loop, and some older
448 // compilers warn when you don't use int for the loop index. The
449 // reason has to do with signed vs. unsigned integer overflow
450 // behavior and optimization.
451 // http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
452 for (int i=0; i<static_cast<int>(actual_threads); i++)
453 {
454#if !LIBMESH_HAVE_OPENMP
455 pthread_create(&threads[i], nullptr, &run_body<Range, Body>, (void *)&range_bodies[i]);
456#else
457 run_body<Range, Body>((void *)&range_bodies[i]);
458#endif
459 }
460
461#if !LIBMESH_HAVE_OPENMP
462 // Wait for them to finish
463 for (unsigned int i=0; i<actual_threads; i++)
464 pthread_join(threads[i], nullptr);
465#endif
466
467 // Join them all down to the original Body
468 for (unsigned int i=actual_threads-1; i != 0; i--)
469 {
470 bodies[i-1]->join(*bodies[i]);
471
472 // Free data ASAP lest we have O(actual_threads^2) at the end
473 managed_bodies[i].reset();
474 }
475}
476
481template <typename Range, typename Body, typename Partitioner>
482inline
483void parallel_reduce (const Range & range, Body & body, const Partitioner &,
484 unsigned int n_threads = libMesh::n_threads())
485{
486 parallel_reduce(range, body, n_threads);
487}
488
489
494template <typename T>
495class atomic
496{
497public:
498 atomic () : val(0) {}
499 operator T () { return val; }
500
502 {
503 spin_mutex::scoped_lock lock(smutex);
504 val = value;
505 return val;
506 }
507
509 {
510 spin_mutex::scoped_lock lock(smutex);
511 val = value;
512 return *this;
513 }
514
515
517 {
518 spin_mutex::scoped_lock lock(smutex);
519 val += value;
520 return val;
521 }
522
524 {
525 spin_mutex::scoped_lock lock(smutex);
526 val -= value;
527 return val;
528 }
529
531 {
532 spin_mutex::scoped_lock lock(smutex);
533 val++;
534 return val;
535 }
536
538 {
539 spin_mutex::scoped_lock lock(smutex);
540 val++;
541 return val;
542 }
543
545 {
546 spin_mutex::scoped_lock lock(smutex);
547 val--;
548 return val;
549 }
550
552 {
553 spin_mutex::scoped_lock lock(smutex);
554 val--;
555 return val;
556 }
557
558private:
561};
562
563} // namespace Threads
564
565} // namespace libMesh
566
567#endif // #ifdef LIBMESH_HAVE_PTHREAD
568
569#endif // LIBMESH_SQUASH_HEADER_WARNING
570
571#endif // LIBMESH_THREADS_PTHREAD_H
Defines atomic operations which can only be executed on a single thread at a time.
atomic< T > & operator=(const atomic< T > &value)
void acquire(recursive_mutex &in_rmutex)
const Elem & get(const ElemType type_in)
bool in_threads
A boolean which is true iff we are in a Threads:: function It may be useful to assert(!...
Definition threads.C:33
int active_threads
An integer which is set to the number of active threads when we are in a Threads:: parallel operation...
Definition threads.C:32
void * run_body(void *args)
tbb::spin_mutex spin_mutex
Spin mutex.
NonConcurrentThread Thread
Use the non-concurrent placeholder.
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
RAIIAcquire< bool, true, true > BoolAcquire
Definition threads.h:84
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
unsigned int num_pthreads(const Range &range, unsigned int requested=libMesh::n_threads())
The libMesh namespace provides an interface to certain functionality in the library.
unsigned int n_threads()
static const bool value
Definition xdr_io.C:55