| 1 | /* |
| 2 | Copyright (c) 2005-2019 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "tbb/atomic.h" |
| 18 | #include "tbb/tick_count.h" |
| 19 | |
| 20 | #ifndef harness_barrier_H |
| 21 | #define harness_barrier_H |
| 22 | |
| 23 | namespace Harness { |
| 24 | |
| 25 | //! Spin WHILE the value of the variable is equal to a given value |
| 26 | /** T and U should be comparable types. */ |
| 27 | class TimedWaitWhileEq { |
| 28 | //! Assignment not allowed |
| 29 | void operator=( const TimedWaitWhileEq& ); |
| 30 | double &my_limit; |
| 31 | public: |
| 32 | TimedWaitWhileEq(double &n_seconds) : my_limit(n_seconds) {} |
| 33 | TimedWaitWhileEq(const TimedWaitWhileEq &src) : my_limit(src.my_limit) {} |
| 34 | template<typename T, typename U> |
| 35 | void operator()( const volatile T& location, U value ) const { |
| 36 | tbb::tick_count start = tbb::tick_count::now(); |
| 37 | double time_passed; |
| 38 | do { |
| 39 | time_passed = (tbb::tick_count::now()-start).seconds(); |
| 40 | if( time_passed < 0.0001 ) __TBB_Pause(10); else __TBB_Yield(); |
| 41 | } while( time_passed < my_limit && location == value); |
| 42 | my_limit -= time_passed; |
| 43 | } |
| 44 | }; |
| 45 | //! Spin WHILE the value of the variable is equal to a given value |
| 46 | /** T and U should be comparable types. */ |
| 47 | class WaitWhileEq { |
| 48 | //! Assignment not allowed |
| 49 | void operator=( const WaitWhileEq& ); |
| 50 | public: |
| 51 | template<typename T, typename U> |
| 52 | void operator()( const volatile T& location, U value ) const { |
| 53 | tbb::internal::spin_wait_while_eq(location, value); |
| 54 | } |
| 55 | }; |
| 56 | class SpinBarrier |
| 57 | { |
| 58 | unsigned numThreads; |
| 59 | tbb::atomic<unsigned> numThreadsFinished; // reached the barrier in this epoch |
| 60 | // the number of times the barrier was opened; TODO: move to a separate cache line |
| 61 | tbb::atomic<unsigned> epoch; |
| 62 | // a throwaway barrier can be used only once, then wait() becomes a no-op |
| 63 | bool throwaway; |
| 64 | |
| 65 | struct DummyCallback { |
| 66 | void operator() () const {} |
| 67 | template<typename T, typename U> |
| 68 | void operator()( const T&, U) const {} |
| 69 | }; |
| 70 | |
| 71 | SpinBarrier( const SpinBarrier& ); // no copy ctor |
| 72 | void operator=( const SpinBarrier& ); // no assignment |
| 73 | public: |
| 74 | SpinBarrier( unsigned nthreads = 0, bool throwaway_ = false ) { |
| 75 | initialize(nthreads, throwaway_); |
| 76 | } |
| 77 | void initialize( unsigned nthreads, bool throwaway_ = false ) { |
| 78 | numThreads = nthreads; |
| 79 | numThreadsFinished = 0; |
| 80 | epoch = 0; |
| 81 | throwaway = throwaway_; |
| 82 | } |
| 83 | |
| 84 | // Returns whether this thread was the last to reach the barrier. |
| 85 | // onWaitCallback is called by a thread for waiting; |
| 86 | // onOpenBarrierCallback is called by the last thread before unblocking other threads. |
| 87 | template<typename WaitEq, typename Callback> |
| 88 | bool custom_wait(const WaitEq &onWaitCallback, const Callback &onOpenBarrierCallback) |
| 89 | { |
| 90 | if (throwaway && epoch) |
| 91 | return false; |
| 92 | unsigned myEpoch = epoch; |
| 93 | unsigned myNumThreads = numThreads; // read it before the increment |
| 94 | int threadsLeft = myNumThreads - numThreadsFinished.fetch_and_increment() - 1; |
| 95 | ASSERT(threadsLeft>=0, "Broken barrier" ); |
| 96 | if (threadsLeft > 0) { |
| 97 | /* this thread is not the last; wait until the epoch changes & return false */ |
| 98 | onWaitCallback(epoch, myEpoch); |
| 99 | return false; |
| 100 | } |
| 101 | /* This thread is the last one at the barrier in this epoch */ |
| 102 | onOpenBarrierCallback(); |
| 103 | /* reset the barrier, increment the epoch, and return true */ |
| 104 | threadsLeft = numThreadsFinished -= myNumThreads; |
| 105 | ASSERT( threadsLeft == 0, "Broken barrier" ); |
| 106 | /* wakes up threads waiting to exit in this epoch */ |
| 107 | myEpoch -= epoch++; |
| 108 | ASSERT( myEpoch == 0, "Broken barrier" ); |
| 109 | return true; |
| 110 | } |
| 111 | bool timed_wait_noerror(double n_seconds) { |
| 112 | custom_wait(TimedWaitWhileEq(n_seconds), DummyCallback()); |
| 113 | return n_seconds >= 0.0001; |
| 114 | } |
| 115 | bool timed_wait(double n_seconds, const char *msg="Time is out while waiting on a barrier" ) { |
| 116 | bool is_last = custom_wait(TimedWaitWhileEq(n_seconds), DummyCallback()); |
| 117 | ASSERT( n_seconds >= 0, msg); // TODO: refactor to avoid passing msg here and rising assertion |
| 118 | return is_last; |
| 119 | } |
| 120 | // onOpenBarrierCallback is called by the last thread before unblocking other threads. |
| 121 | template<typename Callback> |
| 122 | bool wait(const Callback &onOpenBarrierCallback) { |
| 123 | return custom_wait(WaitWhileEq(), onOpenBarrierCallback); |
| 124 | } |
| 125 | bool wait(){ |
| 126 | return wait(DummyCallback()); |
| 127 | } |
| 128 | //! signal to the barrier, rather a semaphore functionality |
| 129 | bool signal_nowait() { |
| 130 | return custom_wait(DummyCallback(),DummyCallback()); |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | } |
| 135 | |
| 136 | #endif //harness_barrier_H |
| 137 | |