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
23namespace Harness {
24
25//! Spin WHILE the value of the variable is equal to a given value
26/** T and U should be comparable types. */
27class TimedWaitWhileEq {
28 //! Assignment not allowed
29 void operator=( const TimedWaitWhileEq& );
30 double &my_limit;
31public:
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. */
47class WaitWhileEq {
48 //! Assignment not allowed
49 void operator=( const WaitWhileEq& );
50public:
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};
56class 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
73public:
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