Line data Source code
1 : // Copyright 2019 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 : #include "src/libplatform/default-worker-threads-task-runner.h"
6 :
7 : #include <vector>
8 :
9 : #include "include/v8-platform.h"
10 : #include "src/base/platform/platform.h"
11 : #include "src/base/platform/semaphore.h"
12 : #include "src/base/platform/time.h"
13 : #include "testing/gtest-support.h"
14 :
15 : namespace v8 {
16 : namespace platform {
17 :
18 46 : class TestTask : public v8::Task {
19 : public:
20 23 : explicit TestTask(std::function<void()> f) : f_(std::move(f)) {}
21 :
22 40 : void Run() override { f_(); }
23 :
24 : private:
25 : std::function<void()> f_;
26 : };
27 :
28 15 : double RealTime() {
29 45 : return base::TimeTicks::HighResolutionNow().ToInternalValue() /
30 30 : static_cast<double>(base::Time::kMicrosecondsPerSecond);
31 : }
32 :
33 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostTaskOrder) {
34 2 : DefaultWorkerThreadsTaskRunner runner(1, RealTime);
35 :
36 : std::vector<int> order;
37 2 : base::Semaphore semaphore(0);
38 :
39 : std::unique_ptr<TestTask> task1 =
40 3 : base::make_unique<TestTask>([&] { order.push_back(1); });
41 : std::unique_ptr<TestTask> task2 =
42 3 : base::make_unique<TestTask>([&] { order.push_back(2); });
43 1 : std::unique_ptr<TestTask> task3 = base::make_unique<TestTask>([&] {
44 2 : order.push_back(3);
45 1 : semaphore.Signal();
46 2 : });
47 :
48 2 : runner.PostTask(std::move(task1));
49 2 : runner.PostTask(std::move(task2));
50 2 : runner.PostTask(std::move(task3));
51 :
52 1 : semaphore.Wait();
53 :
54 1 : runner.Terminate();
55 2 : ASSERT_EQ(3UL, order.size());
56 2 : ASSERT_EQ(1, order[0]);
57 2 : ASSERT_EQ(2, order[1]);
58 2 : ASSERT_EQ(3, order[2]);
59 : }
60 :
61 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostTaskOrderMultipleWorkers) {
62 2 : DefaultWorkerThreadsTaskRunner runner(4, RealTime);
63 :
64 2 : base::Mutex vector_lock;
65 : std::vector<int> order;
66 1 : std::atomic_int count{0};
67 :
68 1 : std::unique_ptr<TestTask> task1 = base::make_unique<TestTask>([&] {
69 1 : base::MutexGuard guard(&vector_lock);
70 2 : order.push_back(1);
71 1 : count++;
72 2 : });
73 1 : std::unique_ptr<TestTask> task2 = base::make_unique<TestTask>([&] {
74 1 : base::MutexGuard guard(&vector_lock);
75 2 : order.push_back(2);
76 1 : count++;
77 2 : });
78 1 : std::unique_ptr<TestTask> task3 = base::make_unique<TestTask>([&] {
79 1 : base::MutexGuard guard(&vector_lock);
80 2 : order.push_back(3);
81 1 : count++;
82 2 : });
83 1 : std::unique_ptr<TestTask> task4 = base::make_unique<TestTask>([&] {
84 1 : base::MutexGuard guard(&vector_lock);
85 2 : order.push_back(4);
86 1 : count++;
87 2 : });
88 1 : std::unique_ptr<TestTask> task5 = base::make_unique<TestTask>([&] {
89 1 : base::MutexGuard guard(&vector_lock);
90 2 : order.push_back(5);
91 1 : count++;
92 2 : });
93 :
94 2 : runner.PostTask(std::move(task1));
95 2 : runner.PostTask(std::move(task2));
96 2 : runner.PostTask(std::move(task3));
97 2 : runner.PostTask(std::move(task4));
98 2 : runner.PostTask(std::move(task5));
99 :
100 : // We can't observe any ordering when there are multiple worker threads. The
101 : // tasks are guaranteed to be dispatched to workers in the input order, but
102 : // the workers are different threads and can be scheduled arbitrarily. Just
103 : // check that all of the tasks were run once.
104 15556 : while (count != 5) {
105 : }
106 :
107 1 : runner.Terminate();
108 2 : ASSERT_EQ(5UL, order.size());
109 2 : ASSERT_EQ(1, std::count(order.begin(), order.end(), 1));
110 2 : ASSERT_EQ(1, std::count(order.begin(), order.end(), 2));
111 2 : ASSERT_EQ(1, std::count(order.begin(), order.end(), 3));
112 2 : ASSERT_EQ(1, std::count(order.begin(), order.end(), 4));
113 2 : ASSERT_EQ(1, std::count(order.begin(), order.end(), 5));
114 : }
115 :
116 : class FakeClock {
117 : public:
118 46 : static double time() { return time_.load(); }
119 : static void set_time(double time) { time_.store(time); }
120 6 : static void set_time_and_wake_up_runner(
121 : double time, DefaultWorkerThreadsTaskRunner* runner) {
122 : time_.store(time);
123 : // PostTask will cause the condition variable WaitFor() call to be notified
124 : // early, rather than waiting for the real amount of time. WaitFor() listens
125 : // to the system clock and not our FakeClock.
126 24 : runner->PostTask(base::make_unique<TestTask>([] {}));
127 6 : }
128 :
129 : private:
130 : static std::atomic<double> time_;
131 : };
132 :
133 : std::atomic<double> FakeClock::time_{0.0};
134 :
135 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostDelayedTaskOrder) {
136 : FakeClock::set_time(0.0);
137 2 : DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time);
138 :
139 : std::vector<int> order;
140 2 : base::Semaphore task1_semaphore(0);
141 2 : base::Semaphore task3_semaphore(0);
142 :
143 1 : std::unique_ptr<TestTask> task1 = base::make_unique<TestTask>([&] {
144 2 : order.push_back(1);
145 1 : task1_semaphore.Signal();
146 2 : });
147 : std::unique_ptr<TestTask> task2 =
148 3 : base::make_unique<TestTask>([&] { order.push_back(2); });
149 1 : std::unique_ptr<TestTask> task3 = base::make_unique<TestTask>([&] {
150 2 : order.push_back(3);
151 1 : task3_semaphore.Signal();
152 2 : });
153 :
154 2 : runner.PostDelayedTask(std::move(task1), 100);
155 2 : runner.PostTask(std::move(task2));
156 2 : runner.PostTask(std::move(task3));
157 :
158 1 : FakeClock::set_time_and_wake_up_runner(99, &runner);
159 :
160 1 : task3_semaphore.Wait();
161 2 : ASSERT_EQ(2UL, order.size());
162 2 : ASSERT_EQ(2, order[0]);
163 2 : ASSERT_EQ(3, order[1]);
164 :
165 1 : FakeClock::set_time_and_wake_up_runner(101, &runner);
166 1 : task1_semaphore.Wait();
167 :
168 1 : runner.Terminate();
169 2 : ASSERT_EQ(3UL, order.size());
170 2 : ASSERT_EQ(2, order[0]);
171 2 : ASSERT_EQ(3, order[1]);
172 2 : ASSERT_EQ(1, order[2]);
173 : }
174 :
175 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostDelayedTaskOrder2) {
176 : FakeClock::set_time(0.0);
177 2 : DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time);
178 :
179 : std::vector<int> order;
180 2 : base::Semaphore task1_semaphore(0);
181 2 : base::Semaphore task2_semaphore(0);
182 2 : base::Semaphore task3_semaphore(0);
183 :
184 1 : std::unique_ptr<TestTask> task1 = base::make_unique<TestTask>([&] {
185 2 : order.push_back(1);
186 1 : task1_semaphore.Signal();
187 2 : });
188 1 : std::unique_ptr<TestTask> task2 = base::make_unique<TestTask>([&] {
189 2 : order.push_back(2);
190 1 : task2_semaphore.Signal();
191 2 : });
192 1 : std::unique_ptr<TestTask> task3 = base::make_unique<TestTask>([&] {
193 2 : order.push_back(3);
194 1 : task3_semaphore.Signal();
195 2 : });
196 :
197 2 : runner.PostDelayedTask(std::move(task1), 500);
198 2 : runner.PostDelayedTask(std::move(task2), 100);
199 2 : runner.PostDelayedTask(std::move(task3), 200);
200 :
201 1 : FakeClock::set_time_and_wake_up_runner(101, &runner);
202 :
203 1 : task2_semaphore.Wait();
204 2 : ASSERT_EQ(1UL, order.size());
205 2 : ASSERT_EQ(2, order[0]);
206 :
207 1 : FakeClock::set_time_and_wake_up_runner(201, &runner);
208 :
209 1 : task3_semaphore.Wait();
210 2 : ASSERT_EQ(2UL, order.size());
211 2 : ASSERT_EQ(2, order[0]);
212 2 : ASSERT_EQ(3, order[1]);
213 :
214 1 : FakeClock::set_time_and_wake_up_runner(501, &runner);
215 :
216 1 : task1_semaphore.Wait();
217 1 : runner.Terminate();
218 2 : ASSERT_EQ(3UL, order.size());
219 2 : ASSERT_EQ(2, order[0]);
220 2 : ASSERT_EQ(3, order[1]);
221 2 : ASSERT_EQ(1, order[2]);
222 : }
223 :
224 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostAfterTerminate) {
225 : FakeClock::set_time(0.0);
226 2 : DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time);
227 :
228 : std::vector<int> order;
229 2 : base::Semaphore task1_semaphore(0);
230 2 : base::Semaphore task2_semaphore(0);
231 2 : base::Semaphore task3_semaphore(0);
232 :
233 1 : std::unique_ptr<TestTask> task1 = base::make_unique<TestTask>([&] {
234 2 : order.push_back(1);
235 1 : task1_semaphore.Signal();
236 2 : });
237 0 : std::unique_ptr<TestTask> task2 = base::make_unique<TestTask>([&] {
238 0 : order.push_back(2);
239 0 : task2_semaphore.Signal();
240 1 : });
241 0 : std::unique_ptr<TestTask> task3 = base::make_unique<TestTask>([&] {
242 0 : order.push_back(3);
243 0 : task3_semaphore.Signal();
244 1 : });
245 :
246 2 : runner.PostTask(std::move(task1));
247 2 : runner.PostDelayedTask(std::move(task2), 100);
248 :
249 1 : task1_semaphore.Wait();
250 2 : ASSERT_EQ(1UL, order.size());
251 2 : ASSERT_EQ(1, order[0]);
252 :
253 1 : runner.Terminate();
254 1 : FakeClock::set_time_and_wake_up_runner(201, &runner);
255 : // OK, we can't actually prove that this never executes. But wait a bit at
256 : // least.
257 : bool signalled =
258 1 : task2_semaphore.WaitFor(base::TimeDelta::FromMilliseconds(100));
259 2 : ASSERT_FALSE(signalled);
260 2 : ASSERT_EQ(1UL, order.size());
261 2 : ASSERT_EQ(1, order[0]);
262 :
263 2 : runner.PostTask(std::move(task3));
264 1 : signalled = task3_semaphore.WaitFor(base::TimeDelta::FromMilliseconds(100));
265 2 : ASSERT_FALSE(signalled);
266 2 : ASSERT_EQ(1UL, order.size());
267 2 : ASSERT_EQ(1, order[0]);
268 : }
269 :
270 15418 : TEST(DefaultWorkerThreadsTaskRunnerUnittest, NoIdleTasks) {
271 2 : DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time);
272 :
273 2 : ASSERT_FALSE(runner.IdleTasksEnabled());
274 1 : runner.Terminate();
275 : }
276 :
277 : } // namespace platform
278 9249 : } // namespace v8
|