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/base/platform/semaphore.h" |
9 #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" | 10 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 static bool old_flag_; | 63 static bool old_flag_; |
64 DISALLOW_COPY_AND_ASSIGN(IgnitionCompilerDispatcherTest); | 64 DISALLOW_COPY_AND_ASSIGN(IgnitionCompilerDispatcherTest); |
65 }; | 65 }; |
66 | 66 |
67 bool IgnitionCompilerDispatcherTest::old_flag_; | 67 bool IgnitionCompilerDispatcherTest::old_flag_; |
68 | 68 |
69 namespace { | 69 namespace { |
70 | 70 |
71 class MockPlatform : public v8::Platform { | 71 class MockPlatform : public v8::Platform { |
72 public: | 72 public: |
73 MockPlatform() : time_(0.0), time_step_(0.0), idle_task_(nullptr), sem_(0) {} | 73 MockPlatform() : idle_task_(nullptr), time_(0.0), time_step_(0.0), sem_(0) {} |
74 ~MockPlatform() override { | 74 ~MockPlatform() override { |
75 base::LockGuard<base::Mutex> lock(&mutex_); | 75 EXPECT_TRUE(tasks_.empty()); |
76 EXPECT_TRUE(foreground_tasks_.empty()); | |
77 EXPECT_TRUE(background_tasks_.empty()); | |
78 EXPECT_TRUE(idle_task_ == nullptr); | 76 EXPECT_TRUE(idle_task_ == nullptr); |
79 } | 77 } |
80 | 78 |
81 size_t NumberOfAvailableBackgroundThreads() override { return 1; } | 79 size_t NumberOfAvailableBackgroundThreads() override { return 1; } |
82 | 80 |
83 void CallOnBackgroundThread(Task* task, | 81 void CallOnBackgroundThread(Task* task, |
84 ExpectedRuntime expected_runtime) override { | 82 ExpectedRuntime expected_runtime) override { |
85 base::LockGuard<base::Mutex> lock(&mutex_); | 83 tasks_.push_back(task); |
86 background_tasks_.push_back(task); | |
87 } | 84 } |
88 | 85 |
89 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { | 86 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { |
90 base::LockGuard<base::Mutex> lock(&mutex_); | 87 UNREACHABLE(); |
91 foreground_tasks_.push_back(task); | |
92 } | 88 } |
93 | 89 |
94 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, | 90 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, |
95 double delay_in_seconds) override { | 91 double delay_in_seconds) override { |
96 UNREACHABLE(); | 92 UNREACHABLE(); |
97 } | 93 } |
98 | 94 |
99 void CallIdleOnForegroundThread(v8::Isolate* isolate, | 95 void CallIdleOnForegroundThread(v8::Isolate* isolate, |
100 IdleTask* task) override { | 96 IdleTask* task) override { |
101 base::LockGuard<base::Mutex> lock(&mutex_); | |
102 ASSERT_TRUE(idle_task_ == nullptr); | 97 ASSERT_TRUE(idle_task_ == nullptr); |
103 idle_task_ = task; | 98 idle_task_ = task; |
104 } | 99 } |
105 | 100 |
106 bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; } | 101 bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; } |
107 | 102 |
108 double MonotonicallyIncreasingTime() override { | 103 double MonotonicallyIncreasingTime() override { |
109 time_ += time_step_; | 104 time_ += time_step_; |
110 return time_; | 105 return time_; |
111 } | 106 } |
112 | 107 |
113 void RunIdleTask(double deadline_in_seconds, double time_step) { | 108 void RunIdleTask(double deadline_in_seconds, double time_step) { |
| 109 ASSERT_TRUE(idle_task_ != nullptr); |
114 time_step_ = time_step; | 110 time_step_ = time_step; |
115 IdleTask* task; | 111 IdleTask* task = idle_task_; |
116 { | 112 idle_task_ = nullptr; |
117 base::LockGuard<base::Mutex> lock(&mutex_); | |
118 task = idle_task_; | |
119 ASSERT_TRUE(idle_task_ != nullptr); | |
120 idle_task_ = nullptr; | |
121 } | |
122 task->Run(deadline_in_seconds); | 113 task->Run(deadline_in_seconds); |
123 delete task; | 114 delete task; |
124 } | 115 } |
125 | 116 |
126 bool IdleTaskPending() { | 117 bool IdleTaskPending() const { return idle_task_; } |
127 base::LockGuard<base::Mutex> lock(&mutex_); | |
128 return idle_task_; | |
129 } | |
130 | 118 |
131 bool BackgroundTasksPending() { | 119 bool BackgroundTasksPending() const { return !tasks_.empty(); } |
132 base::LockGuard<base::Mutex> lock(&mutex_); | |
133 return !background_tasks_.empty(); | |
134 } | |
135 | |
136 bool ForegroundTasksPending() { | |
137 base::LockGuard<base::Mutex> lock(&mutex_); | |
138 return !foreground_tasks_.empty(); | |
139 } | |
140 | 120 |
141 void RunBackgroundTasksAndBlock(Platform* platform) { | 121 void RunBackgroundTasksAndBlock(Platform* platform) { |
142 std::vector<Task*> tasks; | 122 std::vector<Task*> tasks; |
143 { | 123 tasks.swap(tasks_); |
144 base::LockGuard<base::Mutex> lock(&mutex_); | |
145 tasks.swap(background_tasks_); | |
146 } | |
147 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, true), | 124 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, true), |
148 kShortRunningTask); | 125 kShortRunningTask); |
149 sem_.Wait(); | 126 sem_.Wait(); |
150 } | 127 } |
151 | 128 |
152 void RunBackgroundTasks(Platform* platform) { | 129 void RunBackgroundTasks(Platform* platform) { |
153 std::vector<Task*> tasks; | 130 std::vector<Task*> tasks; |
154 { | 131 tasks.swap(tasks_); |
155 base::LockGuard<base::Mutex> lock(&mutex_); | |
156 tasks.swap(background_tasks_); | |
157 } | |
158 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, false), | 132 platform->CallOnBackgroundThread(new TaskWrapper(this, tasks, false), |
159 kShortRunningTask); | 133 kShortRunningTask); |
160 } | 134 } |
161 | 135 |
162 void RunForegroundTasks() { | |
163 std::vector<Task*> tasks; | |
164 { | |
165 base::LockGuard<base::Mutex> lock(&mutex_); | |
166 tasks.swap(foreground_tasks_); | |
167 } | |
168 for (auto& task : tasks) { | |
169 task->Run(); | |
170 delete task; | |
171 } | |
172 } | |
173 | |
174 void ClearBackgroundTasks() { | 136 void ClearBackgroundTasks() { |
175 std::vector<Task*> tasks; | 137 std::vector<Task*> tasks; |
176 { | 138 tasks.swap(tasks_); |
177 base::LockGuard<base::Mutex> lock(&mutex_); | |
178 tasks.swap(background_tasks_); | |
179 } | |
180 for (auto& task : tasks) { | |
181 delete task; | |
182 } | |
183 } | |
184 | |
185 void ClearForegroundTasks() { | |
186 std::vector<Task*> tasks; | |
187 { | |
188 base::LockGuard<base::Mutex> lock(&mutex_); | |
189 tasks.swap(foreground_tasks_); | |
190 } | |
191 for (auto& task : tasks) { | 139 for (auto& task : tasks) { |
192 delete task; | 140 delete task; |
193 } | 141 } |
194 } | 142 } |
195 | 143 |
196 void ClearIdleTask() { | 144 void ClearIdleTask() { |
197 base::LockGuard<base::Mutex> lock(&mutex_); | |
198 ASSERT_TRUE(idle_task_ != nullptr); | 145 ASSERT_TRUE(idle_task_ != nullptr); |
199 delete idle_task_; | 146 delete idle_task_; |
200 idle_task_ = nullptr; | 147 idle_task_ = nullptr; |
201 } | 148 } |
202 | 149 |
203 private: | 150 private: |
204 class TaskWrapper : public Task { | 151 class TaskWrapper : public Task { |
205 public: | 152 public: |
206 TaskWrapper(MockPlatform* platform, const std::vector<Task*>& tasks, | 153 TaskWrapper(MockPlatform* platform, const std::vector<Task*>& tasks, |
207 bool signal) | 154 bool signal) |
208 : platform_(platform), tasks_(tasks), signal_(signal) {} | 155 : platform_(platform), tasks_(tasks), signal_(signal) {} |
209 ~TaskWrapper() = default; | 156 ~TaskWrapper() = default; |
210 | 157 |
211 void Run() override { | 158 void Run() override { |
212 for (auto& task : tasks_) { | 159 for (auto& task : tasks_) { |
213 task->Run(); | 160 task->Run(); |
214 delete task; | 161 delete task; |
215 } | 162 } |
216 if (signal_) platform_->sem_.Signal(); | 163 if (signal_) platform_->sem_.Signal(); |
217 } | 164 } |
218 | 165 |
219 private: | 166 private: |
220 MockPlatform* platform_; | 167 MockPlatform* platform_; |
221 std::vector<Task*> tasks_; | 168 std::vector<Task*> tasks_; |
222 bool signal_; | 169 bool signal_; |
223 | 170 |
224 DISALLOW_COPY_AND_ASSIGN(TaskWrapper); | 171 DISALLOW_COPY_AND_ASSIGN(TaskWrapper); |
225 }; | 172 }; |
226 | 173 |
| 174 IdleTask* idle_task_; |
227 double time_; | 175 double time_; |
228 double time_step_; | 176 double time_step_; |
229 | 177 |
230 // Protects all *_tasks_. | 178 std::vector<Task*> tasks_; |
231 base::Mutex mutex_; | |
232 | |
233 IdleTask* idle_task_; | |
234 std::vector<Task*> background_tasks_; | |
235 std::vector<Task*> foreground_tasks_; | |
236 | |
237 base::Semaphore sem_; | 179 base::Semaphore sem_; |
238 | 180 |
239 DISALLOW_COPY_AND_ASSIGN(MockPlatform); | 181 DISALLOW_COPY_AND_ASSIGN(MockPlatform); |
240 }; | 182 }; |
241 | 183 |
242 } // namespace | 184 } // namespace |
243 | 185 |
244 TEST_F(CompilerDispatcherTest, Construct) { | 186 TEST_F(CompilerDispatcherTest, Construct) { |
245 MockPlatform platform; | 187 MockPlatform platform; |
246 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | 188 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 ASSERT_FALSE(dispatcher.FinishNow(shared)); | 452 ASSERT_FALSE(dispatcher.FinishNow(shared)); |
511 | 453 |
512 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | 454 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); |
513 ASSERT_FALSE(shared->is_compiled()); | 455 ASSERT_FALSE(shared->is_compiled()); |
514 ASSERT_TRUE(i_isolate()->has_pending_exception()); | 456 ASSERT_TRUE(i_isolate()->has_pending_exception()); |
515 | 457 |
516 i_isolate()->clear_pending_exception(); | 458 i_isolate()->clear_pending_exception(); |
517 platform.ClearIdleTask(); | 459 platform.ClearIdleTask(); |
518 } | 460 } |
519 | 461 |
520 TEST_F(IgnitionCompilerDispatcherTest, AsyncAbortAllPendingBackgroundTask) { | |
521 MockPlatform platform; | |
522 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | |
523 | |
524 const char script[] = | |
525 "function g() { var y = 1; function f11(x) { return x * y }; return f11; " | |
526 "} g();"; | |
527 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | |
528 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | |
529 | |
530 ASSERT_FALSE(platform.IdleTaskPending()); | |
531 ASSERT_TRUE(dispatcher.Enqueue(shared)); | |
532 ASSERT_TRUE(platform.IdleTaskPending()); | |
533 | |
534 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | |
535 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
536 CompileJobStatus::kInitial); | |
537 | |
538 // Make compiling super expensive, and advance job as much as possible on the | |
539 // foreground thread. | |
540 dispatcher.tracer_->RecordCompile(50000.0, 1); | |
541 platform.RunIdleTask(10.0, 0.0); | |
542 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
543 CompileJobStatus::kReadyToCompile); | |
544 | |
545 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | |
546 ASSERT_FALSE(shared->is_compiled()); | |
547 ASSERT_FALSE(platform.IdleTaskPending()); | |
548 ASSERT_TRUE(platform.BackgroundTasksPending()); | |
549 | |
550 // The background task hasn't yet started, so we can just cancel it. | |
551 dispatcher.AbortAll(CompilerDispatcher::BlockingBehavior::kDontBlock); | |
552 ASSERT_FALSE(platform.ForegroundTasksPending()); | |
553 | |
554 ASSERT_FALSE(dispatcher.IsEnqueued(shared)); | |
555 ASSERT_FALSE(shared->is_compiled()); | |
556 | |
557 platform.RunBackgroundTasksAndBlock(V8::GetCurrentPlatform()); | |
558 | |
559 if (platform.IdleTaskPending()) platform.ClearIdleTask(); | |
560 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
561 ASSERT_FALSE(platform.ForegroundTasksPending()); | |
562 } | |
563 | |
564 TEST_F(IgnitionCompilerDispatcherTest, AsyncAbortAllRunningBackgroundTask) { | |
565 MockPlatform platform; | |
566 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | |
567 | |
568 const char script1[] = | |
569 "function g() { var y = 1; function f11(x) { return x * y }; return f11; " | |
570 "} g();"; | |
571 Handle<JSFunction> f1 = Handle<JSFunction>::cast(RunJS(isolate(), script1)); | |
572 Handle<SharedFunctionInfo> shared1(f1->shared(), i_isolate()); | |
573 | |
574 const char script2[] = | |
575 "function g() { var y = 1; function f12(x) { return x * y }; return f12; " | |
576 "} g();"; | |
577 Handle<JSFunction> f2 = Handle<JSFunction>::cast(RunJS(isolate(), script2)); | |
578 Handle<SharedFunctionInfo> shared2(f2->shared(), i_isolate()); | |
579 | |
580 ASSERT_FALSE(platform.IdleTaskPending()); | |
581 ASSERT_TRUE(dispatcher.Enqueue(shared1)); | |
582 ASSERT_TRUE(platform.IdleTaskPending()); | |
583 | |
584 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | |
585 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
586 CompileJobStatus::kInitial); | |
587 | |
588 // Make compiling super expensive, and advance job as much as possible on the | |
589 // foreground thread. | |
590 dispatcher.tracer_->RecordCompile(50000.0, 1); | |
591 platform.RunIdleTask(10.0, 0.0); | |
592 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
593 CompileJobStatus::kReadyToCompile); | |
594 | |
595 ASSERT_TRUE(dispatcher.IsEnqueued(shared1)); | |
596 ASSERT_FALSE(shared1->is_compiled()); | |
597 ASSERT_FALSE(platform.IdleTaskPending()); | |
598 ASSERT_TRUE(platform.BackgroundTasksPending()); | |
599 | |
600 // Kick off background tasks and freeze them. | |
601 dispatcher.block_for_testing_.SetValue(true); | |
602 platform.RunBackgroundTasks(V8::GetCurrentPlatform()); | |
603 | |
604 // Busy loop until the background task started running. | |
605 while (dispatcher.block_for_testing_.Value()) { | |
606 } | |
607 dispatcher.AbortAll(CompilerDispatcher::BlockingBehavior::kDontBlock); | |
608 ASSERT_TRUE(platform.ForegroundTasksPending()); | |
609 | |
610 // We can't schedule new tasks while we're aborting. | |
611 ASSERT_FALSE(dispatcher.Enqueue(shared2)); | |
612 | |
613 // Run the first AbortTask. Since the background job is still pending, it | |
614 // can't do anything. | |
615 platform.RunForegroundTasks(); | |
616 { | |
617 base::LockGuard<base::Mutex> lock(&dispatcher.mutex_); | |
618 ASSERT_TRUE(dispatcher.abort_); | |
619 } | |
620 | |
621 // Release background task. | |
622 dispatcher.semaphore_for_testing_.Signal(); | |
623 | |
624 // Busy loop until the background task scheduled another AbortTask task. | |
625 while (!platform.ForegroundTasksPending()) { | |
626 } | |
627 | |
628 platform.RunForegroundTasks(); | |
629 ASSERT_TRUE(dispatcher.jobs_.empty()); | |
630 { | |
631 base::LockGuard<base::Mutex> lock(&dispatcher.mutex_); | |
632 ASSERT_FALSE(dispatcher.abort_); | |
633 } | |
634 | |
635 ASSERT_TRUE(platform.IdleTaskPending()); | |
636 platform.RunIdleTask(5.0, 1.0); | |
637 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
638 ASSERT_FALSE(platform.ForegroundTasksPending()); | |
639 | |
640 // Now it's possible to enqueue new functions again. | |
641 ASSERT_TRUE(dispatcher.Enqueue(shared2)); | |
642 ASSERT_TRUE(platform.IdleTaskPending()); | |
643 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
644 ASSERT_FALSE(platform.ForegroundTasksPending()); | |
645 platform.ClearIdleTask(); | |
646 } | |
647 | |
648 TEST_F(IgnitionCompilerDispatcherTest, FinishNowDuringAbortAll) { | |
649 MockPlatform platform; | |
650 CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size); | |
651 | |
652 const char script[] = | |
653 "function g() { var y = 1; function f13(x) { return x * y }; return f13; " | |
654 "} g();"; | |
655 Handle<JSFunction> f = Handle<JSFunction>::cast(RunJS(isolate(), script)); | |
656 Handle<SharedFunctionInfo> shared(f->shared(), i_isolate()); | |
657 | |
658 ASSERT_FALSE(platform.IdleTaskPending()); | |
659 ASSERT_TRUE(dispatcher.Enqueue(shared)); | |
660 ASSERT_TRUE(platform.IdleTaskPending()); | |
661 | |
662 ASSERT_EQ(dispatcher.jobs_.size(), 1u); | |
663 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
664 CompileJobStatus::kInitial); | |
665 | |
666 // Make compiling super expensive, and advance job as much as possible on the | |
667 // foreground thread. | |
668 dispatcher.tracer_->RecordCompile(50000.0, 1); | |
669 platform.RunIdleTask(10.0, 0.0); | |
670 ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() == | |
671 CompileJobStatus::kReadyToCompile); | |
672 | |
673 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | |
674 ASSERT_FALSE(shared->is_compiled()); | |
675 ASSERT_FALSE(platform.IdleTaskPending()); | |
676 ASSERT_TRUE(platform.BackgroundTasksPending()); | |
677 | |
678 // Kick off background tasks and freeze them. | |
679 dispatcher.block_for_testing_.SetValue(true); | |
680 platform.RunBackgroundTasks(V8::GetCurrentPlatform()); | |
681 | |
682 // Busy loop until the background task started running. | |
683 while (dispatcher.block_for_testing_.Value()) { | |
684 } | |
685 dispatcher.AbortAll(CompilerDispatcher::BlockingBehavior::kDontBlock); | |
686 ASSERT_TRUE(platform.ForegroundTasksPending()); | |
687 | |
688 // Run the first AbortTask. Since the background job is still pending, it | |
689 // can't do anything. | |
690 platform.RunForegroundTasks(); | |
691 { | |
692 base::LockGuard<base::Mutex> lock(&dispatcher.mutex_); | |
693 ASSERT_TRUE(dispatcher.abort_); | |
694 } | |
695 | |
696 // While the background thread holds on to a job, it is still enqueud. | |
697 ASSERT_TRUE(dispatcher.IsEnqueued(shared)); | |
698 | |
699 // Release background task. | |
700 dispatcher.semaphore_for_testing_.Signal(); | |
701 | |
702 // Force the compilation to finish, even while aborting. | |
703 ASSERT_TRUE(dispatcher.FinishNow(shared)); | |
704 ASSERT_TRUE(dispatcher.jobs_.empty()); | |
705 { | |
706 base::LockGuard<base::Mutex> lock(&dispatcher.mutex_); | |
707 ASSERT_FALSE(dispatcher.abort_); | |
708 } | |
709 | |
710 ASSERT_TRUE(platform.ForegroundTasksPending()); | |
711 ASSERT_TRUE(platform.IdleTaskPending()); | |
712 ASSERT_FALSE(platform.BackgroundTasksPending()); | |
713 platform.ClearForegroundTasks(); | |
714 platform.ClearIdleTask(); | |
715 } | |
716 | |
717 } // namespace internal | 462 } // namespace internal |
718 } // namespace v8 | 463 } // namespace v8 |
OLD | NEW |