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 | |