1 | // Copyright 2017 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #ifndef V8_HEAP_BARRIER_H_ |
6 | #define V8_HEAP_BARRIER_H_ |
7 | |
8 | #include "src/base/platform/condition-variable.h" |
9 | #include "src/base/platform/mutex.h" |
10 | #include "src/base/platform/time.h" |
11 | |
12 | namespace v8 { |
13 | namespace internal { |
14 | |
15 | // Barrier that can be used once to synchronize a dynamic number of tasks |
16 | // working concurrently. |
17 | // |
18 | // The barrier takes a timeout which is used to avoid waiting for too long. If |
19 | // any of the users ever reach the timeout they will disable the barrier and |
20 | // signal others to fall through. |
21 | // |
22 | // Usage: |
23 | // void RunConcurrently(OneShotBarrier* shared_barrier) { |
24 | // shared_barrier->Start(); |
25 | // do { |
26 | // { |
27 | // /* process work and create new work */ |
28 | // barrier->NotifyAll(); |
29 | // /* process work and create new work */ |
30 | // } |
31 | // } while(!shared_barrier->Wait()); |
32 | // } |
33 | // |
34 | // Note: If Start() is not called in time, e.g., because the first concurrent |
35 | // task is already done processing all work, then Done() will return true |
36 | // immediately. |
37 | class OneshotBarrier { |
38 | public: |
39 | explicit OneshotBarrier(base::TimeDelta timeout) : timeout_(timeout) {} |
40 | |
41 | void Start() { |
42 | base::MutexGuard guard(&mutex_); |
43 | tasks_++; |
44 | } |
45 | |
46 | void NotifyAll() { |
47 | base::MutexGuard guard(&mutex_); |
48 | if (waiting_ > 0) condition_.NotifyAll(); |
49 | } |
50 | |
51 | bool Wait() { |
52 | base::MutexGuard guard(&mutex_); |
53 | if (done_) return true; |
54 | |
55 | DCHECK_LE(waiting_, tasks_); |
56 | waiting_++; |
57 | if (waiting_ == tasks_) { |
58 | done_ = true; |
59 | condition_.NotifyAll(); |
60 | } else { |
61 | // Spurious wakeup is ok here. |
62 | if (!condition_.WaitFor(&mutex_, timeout_)) { |
63 | // If predefined timeout was reached, Stop waiting and signal being done |
64 | // also to other tasks. |
65 | done_ = true; |
66 | } |
67 | } |
68 | waiting_--; |
69 | return done_; |
70 | } |
71 | |
72 | // Only valid to be called in a sequential setting. |
73 | bool DoneForTesting() const { return done_; } |
74 | |
75 | private: |
76 | base::ConditionVariable condition_; |
77 | base::Mutex mutex_; |
78 | base::TimeDelta timeout_; |
79 | int tasks_ = 0; |
80 | int waiting_ = 0; |
81 | bool done_ = false; |
82 | }; |
83 | |
84 | } // namespace internal |
85 | } // namespace v8 |
86 | |
87 | #endif // V8_HEAP_BARRIER_H_ |
88 | |