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