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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This class defines tests that implementations of SequencedTaskRunner should
// pass in order to be conformant. See task_runner_test_template.h for a
// description of how to use the constructs in this file; these work the same.
#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
#include <cstddef>
#include <iosfwd>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace internal {
struct TaskEvent {
enum Type { POST, START, END };
TaskEvent(int i, Type type);
int i;
Type type;
};
// Utility class used in the tests below.
class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
public:
SequencedTaskTracker();
// Posts the non-nestable task |task|, and records its post event.
void PostWrappedNonNestableTask(
const scoped_refptr<SequencedTaskRunner>& task_runner,
const Closure& task);
// Posts the nestable task |task|, and records its post event.
void PostWrappedNestableTask(
const scoped_refptr<SequencedTaskRunner>& task_runner,
const Closure& task);
// Posts the delayed non-nestable task |task|, and records its post event.
void PostWrappedDelayedNonNestableTask(
const scoped_refptr<SequencedTaskRunner>& task_runner,
const Closure& task,
TimeDelta delay);
// Posts |task_count| non-nestable tasks.
void PostNonNestableTasks(
const scoped_refptr<SequencedTaskRunner>& task_runner,
int task_count);
const std::vector<TaskEvent>& GetTaskEvents() const;
private:
friend class RefCountedThreadSafe<SequencedTaskTracker>;
~SequencedTaskTracker();
// A task which runs |task|, recording the start and end events.
void RunTask(const Closure& task, int task_i);
// Records a post event for task |i|. The owner is expected to be holding
// |lock_| (unlike |TaskStarted| and |TaskEnded|).
void TaskPosted(int i);
// Records a start event for task |i|.
void TaskStarted(int i);
// Records a end event for task |i|.
void TaskEnded(int i);
// Protects events_ and next_post_i_.
Lock lock_;
// The events as they occurred for each task (protected by lock_).
std::vector<TaskEvent> events_;
// The ordinal to be used for the next task-posting task (protected by
// lock_).
int next_post_i_;
DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
};
void PrintTo(const TaskEvent& event, std::ostream* os);
void SleepForOneSecond();
// Checks the non-nestable task invariants for all tasks in |events|.
//
// The invariants are:
// 1) Events started and ended in the same order that they were posted.
// 2) Events for an individual tasks occur in the order {POST, START, END},
// and there is only one instance of each event type for a task.
// 3) The only events between a task's START and END events are the POSTs of
// other tasks. I.e. tasks were run sequentially, not interleaved.
::testing::AssertionResult CheckNonNestableInvariants(
const std::vector<TaskEvent>& events,
int task_count);
} // namespace internal
template <typename TaskRunnerTestDelegate>
class SequencedTaskRunnerTest : public testing::Test {
protected:
SequencedTaskRunnerTest()
: task_tracker_(new internal::SequencedTaskTracker()) {}
const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
TaskRunnerTestDelegate delegate_;
};
TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
// This test posts N non-nestable tasks in sequence, and expects them to run
// in FIFO order, with no part of any two tasks' execution
// overlapping. I.e. that each task starts only after the previously-posted
// one has finished.
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
const int task_count = 1000;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedNonNestableTask(
task_runner, Bind(&internal::SleepForOneSecond));
for (int i = 1; i < task_count; ++i) {
this->task_tracker_->PostWrappedNonNestableTask(task_runner, Closure());
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
task_count));
}
// This test posts N nestable tasks in sequence. It has the same expectations
// as SequentialNonNestable because even though the tasks are nestable, they
// will not be run nestedly in this case.
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
const int task_count = 1000;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedNestableTask(
task_runner,
Bind(&internal::SleepForOneSecond));
for (int i = 1; i < task_count; ++i) {
this->task_tracker_->PostWrappedNestableTask(task_runner, Closure());
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
task_count));
}
// This test posts non-nestable tasks in order of increasing delay, and checks
// that that the tasks are run in FIFO order and that there is no execution
// overlap whatsoever between any two tasks.
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
"non-zero delays; skipping";
return;
}
const int task_count = 20;
const int delay_increment_ms = 50;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
for (int i = 0; i < task_count; ++i) {
this->task_tracker_->PostWrappedDelayedNonNestableTask(
task_runner,
Closure(),
TimeDelta::FromMilliseconds(delay_increment_ms * i));
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
task_count));
}
// This test posts a fast, non-nestable task from within each of a number of
// slow, non-nestable tasks and checks that they all run in the sequence they
// were posted in and that there is no execution overlap whatsoever.
TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
const int parent_count = 10;
const int children_per_parent = 10;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
for (int i = 0; i < parent_count; ++i) {
Closure task = Bind(
&internal::SequencedTaskTracker::PostNonNestableTasks,
this->task_tracker_,
task_runner,
children_per_parent);
this->task_tracker_->PostWrappedNonNestableTask(task_runner, task);
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(
this->task_tracker_->GetTaskEvents(),
parent_count * (children_per_parent + 1)));
}
// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs
// some tasked nestedly (which should be implemented in the test
// delegate). Also add, to the the test delegate, a predicate which checks
// whether the implementation supports nested tasks.
//
REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
SequentialNonNestable,
SequentialNestable,
SequentialDelayedNonNestable,
NonNestablePostFromNonNestableTask);
} // namespace base
#endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_
|