Flutter Engine
waitable_event.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 #include "flutter/fml/synchronization/waitable_event.h"
6 
7 #include <cerrno>
8 #include <ctime>
9 
10 #include "flutter/fml/logging.h"
11 #include "flutter/fml/time/time_delta.h"
12 #include "flutter/fml/time/time_point.h"
13 
14 namespace fml {
15 
16 // Waits with a timeout on |condition()|. Returns true on timeout, or false if
17 // |condition()| ever returns true. |condition()| should have no side effects
18 // (and will always be called with |*mutex| held).
19 template <typename ConditionFn>
20 bool WaitWithTimeoutImpl(std::unique_lock<std::mutex>* locker,
21  std::condition_variable* cv,
22  ConditionFn condition,
23  TimeDelta timeout) {
24  FML_DCHECK(locker->owns_lock());
25 
26  if (condition()) {
27  return false;
28  }
29 
30  // We may get spurious wakeups.
31  TimeDelta wait_remaining = timeout;
32  TimePoint start = TimePoint::Now();
33  while (true) {
34  if (std::cv_status::timeout ==
35  cv->wait_for(*locker, std::chrono::nanoseconds(
36  wait_remaining.ToNanoseconds()))) {
37  return true; // Definitely timed out.
38  }
39 
40  // We may have been awoken.
41  if (condition()) {
42  return false;
43  }
44 
45  // Or the wakeup may have been spurious.
46  TimePoint now = TimePoint::Now();
47  FML_DCHECK(now >= start);
48  TimeDelta elapsed = now - start;
49  // It's possible that we may have timed out anyway.
50  if (elapsed >= timeout) {
51  return true;
52  }
53 
54  // Otherwise, recalculate the amount that we have left to wait.
55  wait_remaining = timeout - elapsed;
56  }
57 }
58 
59 // AutoResetWaitableEvent ------------------------------------------------------
60 
62  std::scoped_lock locker(mutex_);
63  signaled_ = true;
64  cv_.notify_one();
65 }
66 
68  std::scoped_lock locker(mutex_);
69  signaled_ = false;
70 }
71 
73  std::unique_lock<std::mutex> locker(mutex_);
74  while (!signaled_) {
75  cv_.wait(locker);
76  }
77  signaled_ = false;
78 }
79 
81  std::unique_lock<std::mutex> locker(mutex_);
82 
83  if (signaled_) {
84  signaled_ = false;
85  return false;
86  }
87 
88  // We may get spurious wakeups.
89  TimeDelta wait_remaining = timeout;
90  TimePoint start = TimePoint::Now();
91  while (true) {
92  if (std::cv_status::timeout ==
93  cv_.wait_for(
94  locker, std::chrono::nanoseconds(wait_remaining.ToNanoseconds()))) {
95  return true; // Definitely timed out.
96  }
97 
98  // We may have been awoken.
99  if (signaled_) {
100  break;
101  }
102 
103  // Or the wakeup may have been spurious.
104  TimePoint now = TimePoint::Now();
105  FML_DCHECK(now >= start);
106  TimeDelta elapsed = now - start;
107  // It's possible that we may have timed out anyway.
108  if (elapsed >= timeout) {
109  return true;
110  }
111 
112  // Otherwise, recalculate the amount that we have left to wait.
113  wait_remaining = timeout - elapsed;
114  }
115 
116  signaled_ = false;
117  return false;
118 }
119 
121  std::scoped_lock locker(mutex_);
122  return signaled_;
123 }
124 
125 // ManualResetWaitableEvent ----------------------------------------------------
126 
128  std::scoped_lock locker(mutex_);
129  signaled_ = true;
130  signal_id_++;
131  cv_.notify_all();
132 }
133 
135  std::scoped_lock locker(mutex_);
136  signaled_ = false;
137 }
138 
140  std::unique_lock<std::mutex> locker(mutex_);
141 
142  if (signaled_) {
143  return;
144  }
145 
146  auto last_signal_id = signal_id_;
147  do {
148  cv_.wait(locker);
149  } while (signal_id_ == last_signal_id);
150 }
151 
153  std::unique_lock<std::mutex> locker(mutex_);
154 
155  auto last_signal_id = signal_id_;
156  // Disable thread-safety analysis for the lambda: We could annotate it with
157  // |FML_EXCLUSIVE_LOCKS_REQUIRED(mutex_)|, but then the analyzer currently
158  // isn't able to figure out that |WaitWithTimeoutImpl()| calls it while
159  // holding |mutex_|.
160  bool rv = WaitWithTimeoutImpl(
161  &locker, &cv_,
162  [this, last_signal_id]() {
163  // Also check |signaled_| in case we're already signaled.
164  return signaled_ || signal_id_ != last_signal_id;
165  },
166  timeout);
167  FML_DCHECK(rv || signaled_ || signal_id_ != last_signal_id);
168  return rv;
169 }
170 
172  std::scoped_lock locker(mutex_);
173  return signaled_;
174 }
175 
176 } // namespace fml
#define FML_DCHECK(condition)
Definition: logging.h:86
bool WaitWithTimeout(TimeDelta timeout)
Definition: ascii_trie.cc:9
bool WaitWithTimeoutImpl(std::unique_lock< std::mutex > *locker, std::condition_variable *cv, ConditionFn condition, TimeDelta timeout)
constexpr int64_t ToNanoseconds() const
Definition: time_delta.h:61
bool WaitWithTimeout(TimeDelta timeout)
static TimePoint Now()
Definition: time_point.cc:26