1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
base / test / repeating_test_future.h [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TEST_REPEATING_TEST_FUTURE_H_
#define BASE_TEST_REPEATING_TEST_FUTURE_H_
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/test/test_future_internal.h"
#include "base/thread_annotations.h"
namespace base::test {
// DEPRECATED!
//
// Please use `TestFuture` with `TestFuture::GetRepeatingCallback()` instead.
template <typename... Types>
class RepeatingTestFuture {
public:
using TupleType = std::tuple<std::decay_t<Types>...>;
RepeatingTestFuture() = default;
RepeatingTestFuture(const RepeatingTestFuture&) = delete;
RepeatingTestFuture& operator=(const RepeatingTestFuture&) = delete;
RepeatingTestFuture(RepeatingTestFuture&&) = delete;
RepeatingTestFuture& operator=(RepeatingTestFuture&&) = delete;
~RepeatingTestFuture() = default;
void AddValue(Types... values) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
elements_.push(std::make_tuple(std::forward<Types>(values)...));
SignalElementIsAvailable();
}
// Waits until an element is available.
// Returns immediately if one or more elements are already available.
//
// Returns true if an element arrived, or false if a timeout happens.
//
// Directly calling Wait() is not required as Take() will also wait for
// the value to arrive, however you can use a direct call to Wait() to
// improve the error reported:
//
// ASSERT_TRUE(queue.Wait()) << "Detailed error message";
//
[[nodiscard]] bool Wait() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsEmpty()) {
WaitForANewElement();
}
return !IsEmpty();
}
// Returns a callback that when invoked will store all the argument values,
// and unblock any waiters.
// This method is templated so you can specify how you need the arguments to
// be passed - be it const, as reference, or anything you can think off.
// By default the callback accepts the arguments as `Types...`.
//
// Example usage:
//
// RepeatingTestFuture<int, std::string> future;
//
// // returns base::RepeatingCallback<void(int, std::string)>
// future.GetCallback();
//
// // returns base::RepeatingCallback<void(int, const std::string&)>
// future.GetCallback<int, const std::string&>();
//
template <typename... CallbackArgumentsTypes>
base::RepeatingCallback<void(CallbackArgumentsTypes...)> GetCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::BindRepeating(
[](WeakPtr<RepeatingTestFuture<Types...>> future,
CallbackArgumentsTypes... values) {
if (future) {
future->AddValue(std::forward<CallbackArgumentsTypes>(values)...);
}
},
weak_ptr_factory_.GetWeakPtr());
}
base::RepeatingCallback<void(Types...)> GetCallback() {
return GetCallback<Types...>();
}
// Returns true if no elements are currently present. Note that consuming all
// elements through Take() will cause this method to return true after the
// last available element has been consumed.
bool IsEmpty() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return elements_.empty();
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if each element in the future holds a
// single value.
//////////////////////////////////////////////////////////////////////////////
// Wait for an element to arrive, and move its value out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType>
requires(internal::IsSingleValuedTuple<T>)
auto Take() {
return std::get<0>(TakeTuple());
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if each element in the future holds
// multiple values.
//////////////////////////////////////////////////////////////////////////////
// Wait for an element to arrive, and move a tuple with its values out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType>
requires(internal::IsMultiValuedTuple<T>)
TupleType Take() {
return TakeTuple();
}
private:
// Wait until a new element is available.
void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
DCHECK(!run_loop_.has_value());
// Create a new run loop.
run_loop_.emplace();
// Wait until 'run_loop_->Quit()' is called.
run_loop_->Run();
run_loop_.reset();
}
void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
if (run_loop_.has_value()) {
run_loop_->Quit();
}
}
TupleType TakeTuple() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Ensure an element is available.
bool success = Wait();
DCHECK(success) << "Waiting for an element timed out.";
auto result = std::move(elements_.front());
elements_.pop();
return result;
}
base::queue<TupleType> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
// Used by Wait() to know when AddValue() is called.
std::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
};
// Specialization so you can use `RepeatingTestFuture` to wait for a no-args
// callback.
template <>
class RepeatingTestFuture<void> {
public:
void AddValue() { implementation_.AddValue(true); }
// Waits until the callback or `AddValue()` is invoked.
// Returns immediately if one or more invocations have already happened.
//
// Returns true if an invocation arrived, or false if a timeout happens.
//
// Directly calling Wait() is not required as Take() will also wait for
// the invocation to arrive, however you can use a direct call to Wait() to
// improve the error reported:
//
// ASSERT_TRUE(queue.Wait()) << "Detailed error message";
//
[[nodiscard]] bool Wait() { return implementation_.Wait(); }
// Returns a callback that when invoked will unblock any waiters.
base::RepeatingClosure GetCallback() {
return base::BindRepeating(implementation_.GetCallback(), true);
}
// Returns true if no elements are currently present. Note that consuming all
// elements through Take() will cause this method to return true after the
// last available element has been consumed.
bool IsEmpty() const { return implementation_.IsEmpty(); }
// Waits until the callback or `AddValue()` is invoked.
//
// Will DCHECK if a timeout happens.
void Take() { implementation_.Take(); }
private:
RepeatingTestFuture<bool> implementation_;
};
} // namespace base::test
#endif // BASE_TEST_REPEATING_TEST_FUTURE_H_