1 /*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30
31 $Id$
32 */
33 #ifndef MADNESS_WORLD_WORLDMUTEX_H__INCLUDED
34 #define MADNESS_WORLD_WORLDMUTEX_H__INCLUDED
35
36 #include <madness/madness_config.h>
37 #include <pthread.h>
38 #include <cstdio>
39 #ifdef ON_A_MAC
40 #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
41
42 #include <os/lock.h>
43 #include <type_traits>
44
45 typedef std::remove_pointer<os_unfair_lock_t>::type pthread_spinlock_t;
46
pthread_spin_init(pthread_spinlock_t * p,int)47 inline void pthread_spin_init(pthread_spinlock_t* p, int /*mode*/) {
48 *p = OS_UNFAIR_LOCK_INIT;
49 }
pthread_spin_trylock(pthread_spinlock_t * p)50 inline int pthread_spin_trylock(pthread_spinlock_t* p) {
51 return !os_unfair_lock_trylock(p);
52 }
pthread_spin_lock(pthread_spinlock_t * p)53 inline int pthread_spin_lock(pthread_spinlock_t* p) {
54 os_unfair_lock_lock(p);
55 return 0;
56 }
pthread_spin_unlock(pthread_spinlock_t * p)57 inline int pthread_spin_unlock(pthread_spinlock_t* p) {
58 os_unfair_lock_unlock(p);
59 return 0;
60 }
61
pthread_spin_destroy(pthread_spinlock_t *)62 inline int pthread_spin_destroy(pthread_spinlock_t*) {
63 return 0;
64 }
65
66 #else
67
68 #include <libkern/OSAtomic.h>
69 typedef OSSpinLock pthread_spinlock_t;
70
pthread_spin_init(pthread_spinlock_t * p,int)71 inline void pthread_spin_init(pthread_spinlock_t* p, int /*mode*/) {
72 *p=0;
73 }
pthread_spin_trylock(pthread_spinlock_t * p)74 inline int pthread_spin_trylock(pthread_spinlock_t* p) {
75 return !OSSpinLockTry(p);
76 }
pthread_spin_lock(pthread_spinlock_t * p)77 inline int pthread_spin_lock(pthread_spinlock_t* p) {
78 OSSpinLockLock(p);
79 return 0;
80 }
pthread_spin_unlock(pthread_spinlock_t * p)81 inline int pthread_spin_unlock(pthread_spinlock_t* p) {
82 OSSpinLockUnlock(p);
83 return 0;
84 }
pthread_spin_destroy(pthread_spinlock_t *)85 inline void pthread_spin_destroy(pthread_spinlock_t* /*p*/) {}
86 #endif // __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
87 #endif // ON_A_MAC
88
89
90 #include <madness/world/nodefaults.h>
91 #include <madness/world/timers.h>
92 #include <madness/world/atomicint.h>
93 #include <madness/world/madness_exception.h>
94
95 /// \file worldmutex.h
96 /// \brief Implements Mutex, MutexFair, Spinlock, ConditionVariable
97 /// \addtogroup mutexes
98 ///@{
99
100
101
102 namespace madness {
103
104 namespace detail {
105 void print_mutex_error(int error_number);
106 }
107
108 class MutexWaiter {
109 private:
110 unsigned int count;
111
112 /// Yield for specified number of microseconds unless dedicated CPU
113 /// Blue Gene always has a dedicated hw thread
yield(int us)114 void yield(int us) {
115 #if !defined(HAVE_IBMBGP) && !defined(HAVE_IBMBGQ)
116 myusleep(us);
117 #endif
118 }
119
120 public:
MutexWaiter()121 MutexWaiter() : count(0) { }
122
reset()123 void reset() { count = 0; }
124
125 void wait();
126 }; // class MutexWaiter
127
128
129 /// Mutex using pthread mutex operations
130 class Mutex {
131 private:
132 mutable pthread_mutex_t mutex;
133
134 /// Copy constructor is forbidden
135 Mutex(const Mutex&);
136
137 /// Assignment is forbidden
138 void operator=(const Mutex&);
139
140 public:
141 /// Make and initialize a mutex ... initial state is unlocked
142 Mutex(int junk=0) // Junk so that can force initializer in mraX.cc
143 {
144 const int result = pthread_mutex_init(&mutex, 0);
145 if (result) MADNESS_EXCEPTION("failed to initialize mutex", result);
146 }
147
148 /// Try to acquire the mutex ... return true on success, false on failure
try_lock()149 bool try_lock() const {
150 return pthread_mutex_trylock(&mutex)==0;
151 }
152
153 /// Acquire the mutex waiting if necessary
lock()154 void lock() const {
155 const int result = pthread_mutex_lock(&mutex);
156 if (result) {
157 fprintf(stderr, "!! MADNESS ERROR: Mutex::lock() failed acquiring mutex\n");
158 detail::print_mutex_error(result);
159 MADNESS_EXCEPTION("Mutex::lock() failed acquiring mutex", result);
160 }
161 }
162
163 /// Free a mutex owned by this thread
unlock()164 void unlock() const {
165 const int result = pthread_mutex_unlock(&mutex);
166 if (result) {
167 fprintf(stderr, "!! MADNESS ERROR: Mutex::unlock() failed releasing mutex\n");
168 detail::print_mutex_error(result);
169 MADNESS_EXCEPTION("Mutex::unlock() failed releasing mutex", result);
170 }
171 }
172
173 /// Return a pointer to the pthread mutex for use by a condition variable
ptr()174 pthread_mutex_t* ptr() const {
175 return &mutex;
176 }
177
~Mutex()178 virtual ~Mutex() {
179 pthread_mutex_destroy(&mutex);
180 }
181 }; // class Mutex
182
183 /// Recursive mutex using pthread mutex operations
184 class RecursiveMutex {
185 private:
186 mutable pthread_mutex_t mutex;
187
188 /// Copy constructor is forbidden
189 RecursiveMutex(const RecursiveMutex&);
190
191 /// Assignment is forbidden
192 void operator=(const RecursiveMutex&);
193
194 public:
195 /// Make and initialize a mutex ... initial state is unlocked
196 RecursiveMutex();
197
198 /// Try to acquire the mutex ... return true on success, false on failure
try_lock()199 bool try_lock() const {
200 return pthread_mutex_trylock(&mutex)==0;
201 }
202
203 /// Acquire the mutex waiting if necessary
lock()204 void lock() const {
205 int result = pthread_mutex_lock(&mutex);
206 if (result) {
207 fprintf(stderr, "!! MADNESS ERROR: RecursiveMutex::lock() failed acquiring mutex\n");
208 detail::print_mutex_error(result);
209 MADNESS_EXCEPTION("RecursiveMutex::lock() failed acquiring mutex", result);
210 }
211 }
212
213 /// Free a mutex owned by this thread
unlock()214 void unlock() const {
215 int result = pthread_mutex_unlock(&mutex);
216 if (result) {
217 fprintf(stderr, "!! MADNESS ERROR: RecursiveMutex::unlock() failed releasing mutex\n");
218 detail::print_mutex_error(result);
219 MADNESS_EXCEPTION("RecursiveMutex::unlock() failed releasing mutex", result);
220 }
221 }
222
223 /// Return a pointer to the pthread mutex for use by a condition variable
ptr()224 pthread_mutex_t* ptr() const {
225 return &mutex;
226 }
227
~RecursiveMutex()228 ~RecursiveMutex() {
229 pthread_mutex_destroy(&mutex);
230 }
231 }; // class Mutex
232
233
234 /// Mutex that is applied/released at start/end of a scope
235
236 /// The mutex must provide lock and unlock methods
237 template <class mutexT = Mutex>
238 class ScopedMutex {
239 const mutexT* mutex;
240 public:
ScopedMutex(const mutexT * m)241 ScopedMutex(const mutexT* m) : mutex(m) { mutex->lock(); }
242
ScopedMutex(const mutexT & m)243 ScopedMutex(const mutexT& m) : mutex(&m) { mutex->lock(); }
244
~ScopedMutex()245 virtual ~ScopedMutex() { mutex->unlock(); }
246 }; // class ScopedMutex
247
248 #ifdef NEVER_SPIN
249 typedef Mutex Spinlock;
250 #else
251 /// Spinlock using pthread spinlock operations
252 class Spinlock {
253 private:
254 //mutable pthread_spinlock_t spinlock __attribute__ ((aligned (64)));
255 mutable pthread_spinlock_t spinlock;
256
257 /// Copy constructor is forbidden
258 Spinlock(const Spinlock&);
259
260 /// Assignment is forbidden
261 void operator=(const Spinlock&);
262
263 public:
264 /// Make and initialize a spinlock ... initial state is unlocked
265 Spinlock(int junk=0) // Junk so that can force initializer in mraX.cc
266 {
267 pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
268 }
269
270 /// Try to acquire the spinlock ... return true on success, false on failure
try_lock()271 bool try_lock() const {
272 return pthread_spin_trylock(&spinlock)==0;
273 }
274
275 /// Acquire the spinlock waiting if necessary
lock()276 void lock() const {
277 int result = pthread_spin_lock(&spinlock);
278 if (result) {
279 fprintf(stderr, "!! MADNESS ERROR: Spinlock::lock() failed acquiring spinlock\n");
280 detail::print_mutex_error(result);
281 MADNESS_EXCEPTION("Spinlock::lock() failed acquiring spinlock", result);
282 }
283 }
284
285 /// Free a spinlock owned by this thread
unlock()286 void unlock() const {
287 int result = pthread_spin_unlock(&spinlock);
288 if (result) {
289 fprintf(stderr, "!! MADNESS ERROR: Spinlock::unlock() failed releasing spinlock\n");
290 detail::print_mutex_error(result);
291 MADNESS_EXCEPTION("Spinlock::unlock() failed releasing spinlock", result);
292 }
293 }
294
~Spinlock()295 virtual ~Spinlock() {
296 pthread_spin_destroy(&spinlock);
297 }
298 }; // class Spinlock
299 #endif
300
301
302 #define OLDXXX
303 #ifdef OLDXXX
304 // This version uses a spin lock
305 class MutexReaderWriter : private Spinlock, private NO_DEFAULTS {
306 volatile mutable int nreader;
307 volatile mutable bool writeflag;
308 public:
309 static const int NOLOCK=0;
310 static const int READLOCK=1;
311 static const int WRITELOCK=2;
312
MutexReaderWriter()313 MutexReaderWriter() : nreader(0), writeflag(false) {}
314
try_read_lock()315 bool try_read_lock() const {
316 ScopedMutex<Spinlock> protect(this);
317 bool gotit = !writeflag;
318 if (gotit) ++nreader;
319 return gotit;
320 }
321
try_write_lock()322 bool try_write_lock() const {
323 ScopedMutex<Spinlock> protect(this);
324 bool gotit = (!writeflag) && (nreader==0);
325 if (gotit) writeflag = true;
326 return gotit;
327 }
328
try_lock(int lockmode)329 bool try_lock(int lockmode) const {
330 if (lockmode == READLOCK) {
331 return try_read_lock();
332 }
333 else if (lockmode == WRITELOCK) {
334 return try_write_lock();
335 }
336 else if (lockmode == NOLOCK) {
337 return true;
338 }
339 else {
340 MADNESS_EXCEPTION("MutexReaderWriter: try_lock: invalid lock mode", lockmode);
341 }
342 }
343
try_convert_read_lock_to_write_lock()344 bool try_convert_read_lock_to_write_lock() const {
345 ScopedMutex<Spinlock> protect(this);
346 bool gotit = (!writeflag) && (nreader==1);
347 if (gotit) {
348 nreader = 0;
349 writeflag = true;
350 }
351 return gotit;
352 }
353
read_lock()354 void read_lock() const {
355 while (!try_read_lock()) cpu_relax();
356 }
357
write_lock()358 void write_lock() const {
359 while (!try_write_lock()) cpu_relax();
360 }
361
lock(int lockmode)362 void lock(int lockmode) const {
363 while (!try_lock(lockmode)) cpu_relax();
364 }
365
read_unlock()366 void read_unlock() const {
367 ScopedMutex<Spinlock> protect(this);
368 nreader--;
369 }
370
write_unlock()371 void write_unlock() const {
372 // Only a single thread should be setting writeflag but
373 // probably still need the mutex just to get memory fence?
374 ScopedMutex<Spinlock> protect(this);
375 writeflag = false;
376 }
377
unlock(int lockmode)378 void unlock(int lockmode) const {
379 if (lockmode == READLOCK) read_unlock();
380 else if (lockmode == WRITELOCK) write_unlock();
381 else if (lockmode != NOLOCK) MADNESS_EXCEPTION("MutexReaderWriter: try_lock: invalid lock mode", lockmode);
382 }
383
384 /// Converts read to write lock without releasing the read lock
385
386 /// Note that deadlock is guaranteed if two+ threads wait to convert at the same time.
convert_read_lock_to_write_lock()387 void convert_read_lock_to_write_lock() const {
388 while (!try_convert_read_lock_to_write_lock()) cpu_relax();
389 }
390
391 /// Always succeeds immediately
convert_write_lock_to_read_lock()392 void convert_write_lock_to_read_lock() const {
393 ScopedMutex<Spinlock> protect(this);
394 ++nreader;
395 writeflag=false;
396 }
~MutexReaderWriter()397 virtual ~MutexReaderWriter() {}
398 };
399
400 #else
401
402 // This version uses AtomicInt and CAS
403 class MutexReaderWriter : private NO_DEFAULTS {
404 mutable AtomicInt nreader;
405 mutable AtomicInt writeflag;
406 enum {UNLOCKED, LOCKED};
407
408 public:
409 enum lockT {NOLOCK, READLOCK, WRITELOCK};
410
MutexReaderWriter()411 MutexReaderWriter() {nreader=0; writeflag=0;}
412
try_read_lock()413 bool try_read_lock() const {
414 nreader++;
415 if (writeflag == UNLOCKED) return true;
416 nreader--;
417 return false;
418 }
419
try_write_lock()420 bool try_write_lock() const {
421 return (writeflag.compare_and_swap((int) UNLOCKED, (int) LOCKED) == 0);
422 }
423
try_lock(int lockmode)424 bool try_lock(int lockmode) const {
425 if (lockmode == READLOCK) {
426 return try_read_lock();
427 }
428 else if (lockmode == WRITELOCK) {
429 return try_write_lock();
430 }
431 else if (lockmode == NOLOCK) {
432 return true;
433 }
434 else {
435 MADNESS_EXCEPTION("MutexReaderWriter: try_lock: invalid lock mode", lockmode);
436 }
437 }
438
try_convert_read_lock_to_write_lock()439 bool try_convert_read_lock_to_write_lock() const {
440 if (!try_write_lock()) return false;
441 if (nreader > 1) {
442 write_unlock();
443 return false;
444 }
445 nreader = 0;
446 return true;
447 }
448
read_lock()449 void read_lock() const {
450 while (!try_read_lock()) cpu_relax();
451 }
452
write_lock()453 void write_lock() const {
454 while (!try_write_lock()) cpu_relax();
455 }
456
lock(int lockmode)457 void lock(int lockmode) const {
458 while (!try_lock(lockmode)) cpu_relax();
459 }
460
read_unlock()461 void read_unlock() const {
462 nreader--;
463 }
464
write_unlock()465 void write_unlock() const {
466 writeflag = UNLOCKED;
467 }
468
unlock(int lockmode)469 void unlock(int lockmode) const {
470 if (lockmode == READLOCK) read_unlock();
471 else if (lockmode == WRITELOCK) write_unlock();
472 else if (lockmode != NOLOCK) MADNESS_EXCEPTION("MutexReaderWriter: try_lock: invalid lock mode", lockmode);
473 }
474
475 /// Converts read to write lock without releasing the read lock
476
477 /// Note that deadlock is guaranteed if two+ threads wait to convert at the same time.
convert_read_lock_to_write_lock()478 void convert_read_lock_to_write_lock() const {
479 while (!try_convert_read_lock_to_write_lock()) cpu_relax();
480 }
481
482 /// Always succeeds immediately
convert_write_lock_to_read_lock()483 void convert_write_lock_to_read_lock() const {
484 nreader++;
485 writeflag = UNLOCKED;
486 }
487 };
488 #endif
489
490 /// Scalable and fair condition variable (spins on local value)
491 class ConditionVariable : public Spinlock {
492 public:
493 static const int MAX_NTHREAD = 64;
494 mutable volatile int back;
495 mutable volatile int front;
496 mutable volatile bool* volatile q[MAX_NTHREAD]; // Circular buffer of flags
497
498 public:
ConditionVariable()499 ConditionVariable() : back(0), front(0) { }
500
501 /// You should acquire the mutex before waiting
wait()502 void wait() const {
503 // We put a pointer to a thread-local variable at the
504 // end of the queue and wait for that value to be set,
505 // thus generate no memory traffic while waiting.
506 volatile bool myturn = false;
507 int b = back;
508 q[b] = &myturn;
509 ++b;
510 if (b >= MAX_NTHREAD) back = 0;
511 else back = b;
512
513 unlock(); // Release lock before blocking
514 while (!myturn) cpu_relax();
515 lock();
516 }
517
518 /// You should acquire the mutex before signalling
signal()519 void signal() const {
520 if (front != back) {
521 int f = front;
522 int ff = f + 1;
523 if (ff >= MAX_NTHREAD)
524 front = 0;
525 else
526 front = ff;
527
528 *q[f] = true;
529 }
530 }
531
532 /// You should acquire the mutex before broadcasting
broadcast()533 void broadcast() const {
534 while (front != back)
535 signal();
536 }
537
538
~ConditionVariable()539 virtual ~ConditionVariable() {}
540 };
541
542
543 /// A scalable and fair mutex (not recursive)
544
545 /// Needs rewriting to use the CV above and do we really
546 /// need this if using pthread_mutex .. why not pthread_cv?
547 class MutexFair : private Spinlock {
548 private:
549 static const int MAX_NTHREAD = 64;
550 mutable volatile bool* volatile q[MAX_NTHREAD];
551 mutable volatile int n;
552 mutable volatile int front;
553 mutable volatile int back;
554
555 public:
MutexFair()556 MutexFair() : n(0), front(0), back(0) {};
557
lock()558 void lock() const {
559 volatile bool myturn = false;
560 Spinlock::lock();
561 ++n;
562 if (n == 1) {
563 myturn = true;
564 }
565 else {
566 int b = back + 1;
567 if (b >= MAX_NTHREAD) b = 0;
568 q[b] = &myturn;
569 back = b;
570 }
571 Spinlock::unlock();
572
573 while (!myturn) cpu_relax();
574 }
575
unlock()576 void unlock() const {
577 volatile bool* p = 0;
578 Spinlock::lock();
579 n--;
580 if (n > 0) {
581 int f = front + 1;
582 if (f >= MAX_NTHREAD) f = 0;
583 p = q[f];
584 front = f;
585 }
586 Spinlock::unlock();
587 if (p) *p = true;
588 }
589
try_lock()590 bool try_lock() const {
591 bool got_lock;
592
593 Spinlock::lock();
594 int nn = n;
595 got_lock = (nn == 0);
596 if (got_lock) n = nn + 1;
597 Spinlock::unlock();
598
599 return got_lock;
600 }
601 };
602
603
604 /// Attempt to acquire two locks without blocking holding either one
605
606 /// The code will first attempt to acquire mutex m1 and if successful
607 /// will then attempt to acquire mutex m2.
try_two_locks(const Mutex & m1,const Mutex & m2)608 inline bool try_two_locks(const Mutex& m1, const Mutex& m2) {
609 if (!m1.try_lock()) return false;
610 if (m2.try_lock()) return true;
611 m1.unlock();
612 return false;
613 }
614
615
616 /// Simple wrapper for Pthread condition variable with its own mutex
617
618 /// Use this when you need to block without consuming cycles.
619 /// Scheduling granularity is at the level of kernel ticks.
620 class PthreadConditionVariable : private NO_DEFAULTS {
621 private:
622 mutable pthread_cond_t cv;
623 mutable pthread_mutex_t mutex;
624
625 public:
PthreadConditionVariable()626 PthreadConditionVariable() {
627 pthread_cond_init(&cv, nullptr);
628 pthread_mutex_init(&mutex, 0);
629 }
630
get_pthread_mutex()631 pthread_mutex_t& get_pthread_mutex() {
632 return mutex;
633 }
634
lock()635 void lock() const {
636 int result = pthread_mutex_lock(&mutex);
637 if (result) {
638 fprintf(stderr, "!! MADNESS ERROR: PthreadConditionVariable::lock() failed acquiring mutex\n");
639 detail::print_mutex_error(result);
640 MADNESS_EXCEPTION("PthreadConditionVariable::lock() failed acquiring mutex", result);
641 }
642 }
643
unlock()644 void unlock() const {
645 int result = pthread_mutex_unlock(&mutex);
646 if (result) {
647 fprintf(stderr, "!! MADNESS ERROR: PthreadConditionVariable::unlock() failed releasing mutex\n");
648 detail::print_mutex_error(result);
649 MADNESS_EXCEPTION("PthreadConditionVariable::unlock() failed releasing mutex", result);
650 }
651 }
652
653 /// You should have acquired the mutex before entering here
wait()654 void wait() const {
655 pthread_cond_wait(&cv,&mutex);
656 }
657
signal()658 void signal() const {
659 int result = pthread_cond_signal(&cv);
660 if (result) MADNESS_EXCEPTION("ConditionalVariable: signalling failed", result);
661 }
662
broadcast()663 void broadcast() const {
664 int result = pthread_cond_broadcast(&cv);
665 if (result) MADNESS_EXCEPTION("ConditionalVariable: signalling failed", result);
666 }
667
~PthreadConditionVariable()668 virtual ~PthreadConditionVariable() {
669 pthread_mutex_destroy(&mutex);
670 pthread_cond_destroy(&cv);
671 }
672 }; // class PthreadConditionVariable
673
674 #ifdef USE_SPINLOCKS
675 typedef ConditionVariable CONDITION_VARIABLE_TYPE ;
676 typedef Spinlock SPINLOCK_TYPE;
677 typedef MutexFair SCALABLE_MUTEX_TYPE;
678 #else
679 typedef PthreadConditionVariable CONDITION_VARIABLE_TYPE;
680 typedef Mutex SPINLOCK_TYPE;
681 typedef Mutex SCALABLE_MUTEX_TYPE;
682 #endif
683
684 class Barrier {
685 const int nthread;
686 volatile bool sense;
687 AtomicInt nworking;
688 volatile bool* pflags[64];
689
690 public:
Barrier(int nthread)691 Barrier(int nthread)
692 : nthread(nthread)
693 , sense(true)
694 {
695 nworking = nthread;
696 }
697
698 /// Each thread calls this once before first use
699
700 /// id should be the thread id (0,..,nthread-1) and pflag a pointer to
701 /// thread-local bool (probably in the thread's stack)
register_thread(int id,volatile bool * pflag)702 void register_thread(int id, volatile bool* pflag) {
703 if (id > 63) MADNESS_EXCEPTION("Barrier : hard dimension failed", id);
704 pflags[id] = pflag;
705 *pflag=!sense;
706 }
707
708 /// Each thread calls this with its id (0,..,nthread-1) to enter the barrier
709
710 /// The thread last to enter the barrier returns true. Others return false.
711 ///
712 /// All calls to the barrier must use the same value of nthread.
enter(const int id)713 bool enter(const int id) {
714 if (nthread <= 1) {
715 return true;
716 }
717 else {
718 if (id > 63) MADNESS_EXCEPTION("Barrier : hard dimension failed", id);
719 bool lsense = sense; // Local copy of sense
720 bool result = nworking.dec_and_test();
721 if (result) {
722 // Reset counter and sense for next entry
723 nworking = nthread;
724 sense = !sense;
725 __asm__ __volatile__("" : : : "memory");
726
727 // Notify everyone including me
728 for (int i = 0; i < nthread; ++i)
729 *(pflags[i]) = lsense;
730 } else {
731 volatile bool* myflag = pflags[id]; // Local flag;
732 while (*myflag != lsense) {
733 cpu_relax();
734 }
735 }
736 return result;
737 }
738 }
739 }; // class Barrier
740
741 namespace detail {
742 extern Mutex printmutex;
743 }
744 }
745
746 ///@}
747
748
749 #endif // MADNESS_WORLD_WORLDMUTEX_H__INCLUDED
750