OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler-dispatcher/compiler-dispatcher.h" | 5 #include "src/compiler-dispatcher/compiler-dispatcher.h" |
6 | 6 |
7 #include "include/v8-platform.h" | 7 #include "include/v8-platform.h" |
8 #include "src/base/platform/semaphore.h" | |
8 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" | 9 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" |
10 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" | |
9 #include "src/flags.h" | 11 #include "src/flags.h" |
10 #include "src/handles.h" | 12 #include "src/handles.h" |
11 #include "src/objects-inl.h" | 13 #include "src/objects-inl.h" |
14 #include "src/v8.h" | |
12 #include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h" | 15 #include "test/unittests/compiler-dispatcher/compiler-dispatcher-helper.h" |
13 #include "test/unittests/test-utils.h" | 16 #include "test/unittests/test-utils.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
15 | 18 |
16 namespace v8 { | 19 namespace v8 { |
17 namespace internal { | 20 namespace internal { |
18 | 21 |
19 class CompilerDispatcherTest : public TestWithContext { | 22 class CompilerDispatcherTest : public TestWithContext { |
20 public: | 23 public: |
21 CompilerDispatcherTest() = default; | 24 CompilerDispatcherTest() = default; |
(...skipping 11 matching lines...) Expand all Loading... | |
33 } | 36 } |
34 | 37 |
35 private: | 38 private: |
36 static bool old_flag_; | 39 static bool old_flag_; |
37 | 40 |
38 DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherTest); | 41 DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherTest); |
39 }; | 42 }; |
40 | 43 |
41 bool CompilerDispatcherTest::old_flag_; | 44 bool CompilerDispatcherTest::old_flag_; |
42 | 45 |
46 class IgnitionCompilerDispatcherTest : public CompilerDispatcherTest { | |
47 public: | |
48 IgnitionCompilerDispatcherTest() = default; | |
49 ~IgnitionCompilerDispatcherTest() override = default; | |
50 | |
51 static void SetUpTestCase() { | |
52 old_flag_ = i::FLAG_ignition; | |
53 i::FLAG_ignition = true; | |
vogelheim
2017/01/03 10:32:37
Is this necessary?
unittests/ uses gtest, which h
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
I never heard of that? how does it work?
I need t
| |
54 CompilerDispatcherTest::SetUpTestCase(); | |
55 } | |
56 | |
57 static void TearDownTestCase() { | |
58 CompilerDispatcherTest::TearDownTestCase(); | |
59 i::FLAG_ignition = old_flag_; | |
60 } | |
61 | |
62 private: | |
63 static bool old_flag_; | |
64 DISALLOW_COPY_AND_ASSIGN(IgnitionCompilerDispatcherTest); | |
65 }; | |
66 | |
67 bool IgnitionCompilerDispatcherTest::old_flag_; | |
68 | |
43 namespace { | 69 namespace { |
44 | 70 |
45 class MockPlatform : public v8::Platform { | 71 class MockPlatform : public v8::Platform { |
46 public: | 72 public: |
47 MockPlatform() : task_(nullptr), time_(0.0), time_step_(0.0) {} | 73 MockPlatform() : idle_task_(nullptr), time_(0.0), time_step_(0.0), sem_(0) {} |
48 ~MockPlatform() override = default; | 74 ~MockPlatform() override { |
75 EXPECT_TRUE(tasks_.empty()); | |
76 EXPECT_TRUE(idle_task_ == nullptr); | |
77 } | |
78 | |
79 size_t NumberOfAvailableBackgroundThreads() override { return 1; } | |
49 | 80 |
50 void CallOnBackgroundThread(Task* task, | 81 void CallOnBackgroundThread(Task* task, |
51 ExpectedRuntime expected_runtime) override { | 82 ExpectedRuntime expected_runtime) override { |
52 UNREACHABLE(); | 83 tasks_.push_back(task); |
53 } | 84 } |
54 | 85 |
55 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { | 86 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { |
56 UNREACHABLE(); | 87 UNREACHABLE(); |
57 } | 88 } |
58 | 89 |
59 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, | 90 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, |
60 double delay_in_seconds) override { | 91 double delay_in_seconds) override { |
61 UNREACHABLE(); | 92 UNREACHABLE(); |
62 } | 93 } |
63 | 94 |
64 void CallIdleOnForegroundThread(v8::Isolate* isolate, | 95 void CallIdleOnForegroundThread(v8::Isolate* isolate, |
65 IdleTask* task) override { | 96 IdleTask* task) override { |
66 task_ = task; | 97 idle_task_ = task; |
vogelheim
2017/01/03 10:32:37
ASSERT_TRUE(idle_task_ == nullptr) before this lin
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
done
| |
67 } | 98 } |
68 | 99 |
69 bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; } | 100 bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; } |
70 | 101 |
71 double MonotonicallyIncreasingTime() override { | 102 double MonotonicallyIncreasingTime() override { |
72 time_ += time_step_; | 103 time_ += time_step_; |
73 return time_; | 104 return time_; |
74 } | 105 } |
75 | 106 |
76 void RunIdleTask(double deadline_in_seconds, double time_step) { | 107 void RunIdleTask(double deadline_in_seconds, double time_step) { |
77 ASSERT_TRUE(task_ != nullptr); | 108 ASSERT_TRUE(idle_task_ != nullptr); |
78 time_step_ = time_step; | 109 time_step_ = time_step; |
79 IdleTask* task = task_; | 110 IdleTask* task = idle_task_; |
80 task_ = nullptr; | 111 idle_task_ = nullptr; |
81 task->Run(deadline_in_seconds); | 112 task->Run(deadline_in_seconds); |
82 delete task; | 113 delete task; |
83 } | 114 } |
84 | 115 |
85 bool IdleTaskPending() const { return !!task_; } | 116 bool IdleTaskPending() const { return !!idle_task_; } |
vogelheim
2017/01/03 10:32:37
nitpick: The !! is a no-op here, since pointer-to-
| |
117 | |
118 bool BackgroundTasksPending() const { return !tasks_.empty(); } | |
119 | |
120 void RunBackgroundTasksAndBlock(Platform* platform) { | |
121 std::vector<Task*> tasks; | |
122 tasks.swap(tasks_); | |
123 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, true), | |
124 kShortRunningTask); | |
125 sem_.Wait(); | |
126 } | |
127 | |
128 void RunBackgroundTasks(Platform* platform) { | |
129 std::vector<Task*> tasks; | |
130 tasks.swap(tasks_); | |
131 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, false), | |
132 kShortRunningTask); | |
133 } | |
134 | |
135 void ClearBackgroundTasks() { | |
136 std::vector<Task*> tasks; | |
137 tasks.swap(tasks_); | |
138 for (auto&& task : tasks) { | |
vogelheim
2017/01/03 10:32:37
C++ question: Why the auto&& ?
(I take it this ma
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
no reason actually, using auto& now
| |
139 delete task; | |
140 } | |
141 } | |
142 | |
143 void ClearIdleTask() { | |
144 ASSERT_TRUE(idle_task_ != nullptr); | |
145 delete idle_task_; | |
146 idle_task_ = nullptr; | |
147 } | |
86 | 148 |
87 private: | 149 private: |
88 IdleTask* task_; | 150 class TaskWrapper : public Task { |
151 public: | |
152 TaskWrapper(MockPlatform* platform, const std::vector<Task*>& tasks, | |
153 bool signal) | |
154 : platform_(platform), tasks_(tasks), signal_(signal) {} | |
155 ~TaskWrapper() = default; | |
156 | |
157 void Run() override { | |
158 for (auto&& task : tasks_) { | |
159 task->Run(); | |
160 delete task; | |
161 } | |
162 if (signal_) platform_->sem_.Signal(); | |
163 } | |
164 | |
165 private: | |
166 MockPlatform* platform_; | |
167 std::vector<Task*> tasks_; | |
168 bool signal_; | |
169 | |
170 DISALLOW_COPY_AND_ASSIGN(TaskWrapper); | |
171 }; | |
172 | |
173 IdleTask* idle_task_; | |
89 double time_; | 174 double time_; |
90 double time_step_; | 175 double time_step_; |
91 | 176 |
177 std::vector<Task*> tasks_; | |
178 base::Semaphore sem_; | |
179 | |
92 DISALLOW_COPY_AND_ASSIGN(MockPlatform); | 180 DISALLOW_COPY_AND_ASSIGN(MockPlatform); |
93 }; | 181 }; |
94 | 182 |
95 } // namespace | 183 } // namespace |
96 | 184 |
97 TEST_F(CompilerDispatcherTest, Construct) { | 185 TEST_F(CompilerDispatcherTest, Construct) { |
98 MockPlatform platform; | 186 MockPlatform platform; |
99 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | 187 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); |
100 } | 188 } |
101 | 189 |
102 TEST_F(CompilerDispatcherTest, IsEnqueued) { | 190 TEST_F(CompilerDispatcherTest, IsEnqueued) { |
103 MockPlatform platform; | 191 MockPlatform platform; |
104 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | 192 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); |
105 | 193 |
106 const char script[] = | 194 const char script[] = |
107 "function g() { var y = 1; function f1(x) { return x * y }; return f1; } " | 195 "function g() { var y = 1; function f1(x) { return x * y }; return f1; } " |
108 "g();"; | 196 "g();"; |
109 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | 197 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); |
110 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | 198 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); |
111 | 199 |
112 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 200 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
113 ASSERT_TRUE(dispatcher.Enqueue(shared)); | 201 ASSERT_TRUE(dispatcher.Enqueue(shared)); |
114 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | 202 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); |
115 dispatcher.Abort(shared, CompilerDispatcher::BlockingBehavior::kBlock); | 203 dispatcher.AbortAll(CompilerDispatcher::BlockingBehavior::kBlock); |
116 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 204 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
205 ASSERT_TRUE(platform.IdleTaskPending()); | |
206 platform.ClearIdleTask(); | |
117 } | 207 } |
118 | 208 |
119 TEST_F(CompilerDispatcherTest, FinishNow) { | 209 TEST_F(CompilerDispatcherTest, FinishNow) { |
120 MockPlatform platform; | 210 MockPlatform platform; |
121 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | 211 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); |
122 | 212 |
123 const char script[] = | 213 const char script[] = |
124 "function g() { var y = 1; function f2(x) { return x * y }; return f2; } " | 214 "function g() { var y = 1; function f2(x) { return x * y }; return f2; } " |
125 "g();"; | 215 "g();"; |
126 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | 216 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); |
127 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | 217 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); |
128 | 218 |
129 ASSERT_FALSE(shared->is_compiled()); | 219 ASSERT_FALSE(shared->is_compiled()); |
130 ASSERT_TRUE(dispatcher.Enqueue(shared)); | 220 ASSERT_TRUE(dispatcher.Enqueue(shared)); |
131 ASSERT_TRUE(dispatcher.FinishNow(shared)); | 221 ASSERT_TRUE(dispatcher.FinishNow(shared)); |
132 // Finishing removes the SFI from the queue. | 222 // Finishing removes the SFI from the queue. |
133 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 223 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
134 ASSERT_TRUE(shared->is_compiled()); | 224 ASSERT_TRUE(shared->is_compiled()); |
225 ASSERT_TRUE(platform.IdleTaskPending()); | |
226 platform.ClearIdleTask(); | |
135 } | 227 } |
136 | 228 |
137 TEST_F(CompilerDispatcherTest, IdleTask) { | 229 TEST_F(CompilerDispatcherTest, IdleTask) { |
138 MockPlatform platform; | 230 MockPlatform platform; |
139 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | 231 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); |
140 | 232 |
141 const char script[] = | 233 const char script[] = |
142 "function g() { var y = 1; function f3(x) { return x * y }; return f3; } " | 234 "function g() { var y = 1; function f3(x) { return x * y }; return f3; } " |
143 "g();"; | 235 "g();"; |
144 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | 236 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
181 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | 273 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); |
182 ASSERT_FALSE(shared->is_compiled()); | 274 ASSERT_FALSE(shared->is_compiled()); |
183 ASSERT_TRUE(platform.IdleTaskPending()); | 275 ASSERT_TRUE(platform.IdleTaskPending()); |
184 | 276 |
185 // The job should be still scheduled for the main thread, but ready for | 277 // The job should be still scheduled for the main thread, but ready for |
186 // parsing. | 278 // parsing. |
187 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | 279 ASSERT_EQ(dispatcher.jobs_.size(), 1u); |
188 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | 280 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == |
189 CompileJobStatus::kReadyToParse); | 281 CompileJobStatus::kReadyToParse); |
190 | 282 |
191 // Only grant a lot of idle time and freeze time. | 283 // Now grant a lot of idle time and freeze time. |
192 platform.RunIdleTask(1000.0, 0.0); | 284 platform.RunIdleTask(1000.0, 0.0); |
193 | 285 |
194 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 286 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
195 ASSERT_TRUE(shared->is_compiled()); | 287 ASSERT_TRUE(shared->is_compiled()); |
196 ASSERT_FALSE(platform.IdleTaskPending()); | 288 ASSERT_FALSE(platform.IdleTaskPending()); |
197 } | 289 } |
198 | 290 |
199 TEST_F(CompilerDispatcherTest, IdleTaskException) { | 291 TEST_F(CompilerDispatcherTest, IdleTaskException) { |
200 MockPlatform platform; | 292 MockPlatform platform; |
201 CompilerDispatcher dispatcher(i_isolate(), &platform, 50); | 293 CompilerDispatcher dispatcher(i_isolate(), &platform, 50); |
(...skipping 16 matching lines...) Expand all Loading... | |
218 | 310 |
219 // Since time doesn't progress on the MockPlatform, this is enough idle time | 311 // Since time doesn't progress on the MockPlatform, this is enough idle time |
220 // to finish compiling the function. | 312 // to finish compiling the function. |
221 platform.RunIdleTask(1000.0, 0.0); | 313 platform.RunIdleTask(1000.0, 0.0); |
222 | 314 |
223 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 315 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
224 ASSERT_FALSE(shared->is_compiled()); | 316 ASSERT_FALSE(shared->is_compiled()); |
225 ASSERT_FALSE(try_catch.HasCaught()); | 317 ASSERT_FALSE(try_catch.HasCaught()); |
226 } | 318 } |
227 | 319 |
320 TEST_F(IgnitionCompilerDispatcherTest, CompileOnBackgroundThread) { | |
321 MockPlatform platform; | |
marja
2017/01/03 10:42:41
You could make MockPlatform a member of CompilerDi
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
meh, unless you feel strongly about this
marja
2017/01/03 13:14:57
I don't :)
| |
322 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | |
323 | |
324 const char script[] = | |
325 "function g() { var y = 1; function f6(x) { return x * y }; return f6; } " | |
326 "g();"; | |
327 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | |
328 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | |
329 | |
330 ASSERT_FALSE(platform.IdleTaskPending()); | |
331 ASSERT_TRUE(dispatcher.Enqueue(shared)); | |
332 ASSERT_TRUE(platform.IdleTaskPending()); | |
333 | |
334 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | |
335 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
336 CompileJobStatus::kInitial); | |
337 | |
338 // Make compiling super expensive, and advance job as much as possible on the | |
339 // foreground thread. | |
340 dispatcher.tracer_->RecordCompile(50000.0, 1); | |
341 platform.RunIdleTask(10.0, 0.0); | |
342 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
343 CompileJobStatus::kReadyToCompile); | |
344 | |
345 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | |
346 ASSERT_FALSE(shared->is_compiled()); | |
347 ASSERT_FALSE(platform.IdleTaskPending()); | |
348 ASSERT_TRUE(platform.BackgroundTasksPending()); | |
349 | |
350 platform.RunBackgroundTasksAndBlock(V8::GetCurrentPlatform()); | |
351 | |
352 ASSERT_TRUE(platform.IdleTaskPending()); | |
353 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
354 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
355 CompileJobStatus::kCompiled); | |
356 | |
357 // Now grant a lot of idle time and freeze time. | |
358 platform.RunIdleTask(1000.0, 0.0); | |
vogelheim
2017/01/03 10:32:37
[For my understanding:] What does this step accomp
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
this step does the final step of compilation which
| |
359 | |
360 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | |
361 ASSERT_TRUE(shared->is_compiled()); | |
362 ASSERT_FALSE(platform.IdleTaskPending()); | |
363 } | |
364 | |
365 TEST_F(IgnitionCompilerDispatcherTest, FinishNowWithBackgroundTask) { | |
366 MockPlatform platform; | |
367 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | |
368 | |
369 const char script[] = | |
370 "function g() { var y = 1; function f7(x) { return x * y }; return f7; } " | |
371 "g();"; | |
372 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | |
373 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | |
374 | |
375 ASSERT_FALSE(platform.IdleTaskPending()); | |
376 ASSERT_TRUE(dispatcher.Enqueue(shared)); | |
377 ASSERT_TRUE(platform.IdleTaskPending()); | |
378 | |
379 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | |
380 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
381 CompileJobStatus::kInitial); | |
382 | |
383 // Make compiling super expensive, and advance job as much as possible on the | |
384 // foreground thread. | |
385 dispatcher.tracer_->RecordCompile(50000.0, 1); | |
386 platform.RunIdleTask(10.0, 0.0); | |
387 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
388 CompileJobStatus::kReadyToCompile); | |
389 | |
390 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | |
391 ASSERT_FALSE(shared->is_compiled()); | |
392 ASSERT_FALSE(platform.IdleTaskPending()); | |
393 ASSERT_TRUE(platform.BackgroundTasksPending()); | |
394 | |
395 platform.RunBackgroundTasks(V8::GetCurrentPlatform()); | |
marja
2017/01/03 10:42:41
For documenting what happens, you could assert the
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
RunBackgroundTasks() does not block, so this test
| |
396 | |
397 ASSERT_TRUE(dispatcher.FinishNow(shared)); | |
398 // Finishing removes the SFI from the queue. | |
399 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | |
400 ASSERT_TRUE(shared->is_compiled()); | |
401 ASSERT_FALSE(platform.IdleTaskPending()); | |
402 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
403 } | |
404 | |
228 } // namespace internal | 405 } // namespace internal |
229 } // namespace v8 | 406 } // namespace v8 |
OLD | NEW |