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" | |
8 #include "include/v8.h" | |
9 #include "src/base/platform/time.h" | |
10 #include "src/cancelable-task.h" | |
7 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" | 11 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" |
8 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" | 12 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" |
9 #include "src/objects-inl.h" | 13 #include "src/objects-inl.h" |
10 | 14 |
11 namespace v8 { | 15 namespace v8 { |
12 namespace internal { | 16 namespace internal { |
13 | 17 |
14 namespace { | 18 namespace { |
15 | 19 |
16 bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { | 20 enum class ExceptionHandling { kSwallow, kKeep }; |
21 | |
22 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job, | |
23 ExceptionHandling exception_handling) { | |
24 DCHECK(ThreadId::Current().Equals(isolate->thread_id())); | |
25 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); | |
17 switch (job->status()) { | 26 switch (job->status()) { |
18 case CompileJobStatus::kInitial: | 27 case CompileJobStatus::kInitial: |
19 job->PrepareToParseOnMainThread(); | 28 job->PrepareToParseOnMainThread(); |
20 break; | 29 break; |
21 | 30 |
22 case CompileJobStatus::kReadyToParse: | 31 case CompileJobStatus::kReadyToParse: |
23 job->Parse(); | 32 job->Parse(); |
24 break; | 33 break; |
25 | 34 |
26 case CompileJobStatus::kParsed: | 35 case CompileJobStatus::kParsed: |
(...skipping 10 matching lines...) Expand all Loading... | |
37 | 46 |
38 case CompileJobStatus::kCompiled: | 47 case CompileJobStatus::kCompiled: |
39 job->FinalizeCompilingOnMainThread(); | 48 job->FinalizeCompilingOnMainThread(); |
40 break; | 49 break; |
41 | 50 |
42 case CompileJobStatus::kFailed: | 51 case CompileJobStatus::kFailed: |
43 case CompileJobStatus::kDone: | 52 case CompileJobStatus::kDone: |
44 break; | 53 break; |
45 } | 54 } |
46 | 55 |
56 if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) { | |
57 DCHECK(job->status() == CompileJobStatus::kFailed); | |
vogelheim
2016/12/15 15:24:12
nitpick: DCHECK_EQ
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
that would require implementation operator<< for t
| |
58 try_catch.ReThrow(); | |
59 } | |
47 return job->status() != CompileJobStatus::kFailed; | 60 return job->status() != CompileJobStatus::kFailed; |
48 } | 61 } |
49 | 62 |
50 bool IsFinished(CompilerDispatcherJob* job) { | 63 bool IsFinished(CompilerDispatcherJob* job) { |
51 return job->status() == CompileJobStatus::kDone || | 64 return job->status() == CompileJobStatus::kDone || |
52 job->status() == CompileJobStatus::kFailed; | 65 job->status() == CompileJobStatus::kFailed; |
53 } | 66 } |
54 | 67 |
68 // Theoretically, we get 50ms of idle time max, however, it's unlikely that | |
vogelheim
2016/12/15 15:24:11
super nitpick: Remove some of the commas, like:
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
done
| |
69 // we'll get all of it, so try to be a conservative. | |
70 const double kMaxIdleTimeToExpectInMs = 40; | |
71 | |
55 } // namespace | 72 } // namespace |
56 | 73 |
57 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size) | 74 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
75 public: | |
76 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); | |
77 ~IdleTask() override; | |
78 | |
79 // CancelableIdleTask implementation. | |
80 void RunInternal(double deadline_in_seconds) override; | |
81 | |
82 private: | |
83 CompilerDispatcher* dispatcher_; | |
84 | |
85 DISALLOW_COPY_AND_ASSIGN(IdleTask); | |
86 }; | |
87 | |
88 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | |
89 CompilerDispatcher* dispatcher) | |
90 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} | |
91 | |
92 CompilerDispatcher::IdleTask::~IdleTask() {} | |
93 | |
94 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | |
95 dispatcher_->DoIdleWork(deadline_in_seconds); | |
96 } | |
97 | |
98 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | |
99 size_t max_stack_size) | |
58 : isolate_(isolate), | 100 : isolate_(isolate), |
101 platform_(platform), | |
59 max_stack_size_(max_stack_size), | 102 max_stack_size_(max_stack_size), |
60 tracer_(new CompilerDispatcherTracer(isolate_)) {} | 103 tracer_(new CompilerDispatcherTracer(isolate_)), |
104 idle_task_scheduled_(false) {} | |
61 | 105 |
62 CompilerDispatcher::~CompilerDispatcher() {} | 106 CompilerDispatcher::~CompilerDispatcher() { |
107 // To avoid crashing in unit tests due to unfished jobs. | |
108 AbortAll(BlockingBehavior::kBlock); | |
109 } | |
63 | 110 |
64 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 111 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
65 // We only handle functions (no eval / top-level code / wasm) that are | 112 // We only handle functions (no eval / top-level code / wasm) that are |
66 // attached to a script. | 113 // attached to a script. |
67 if (!function->script()->IsScript() || !function->is_function() || | 114 if (!function->script()->IsScript() || !function->is_function() || |
68 function->asm_function() || function->native()) { | 115 function->asm_function() || function->native()) { |
69 return false; | 116 return false; |
70 } | 117 } |
71 | 118 |
72 if (IsEnqueued(function)) return true; | 119 if (IsEnqueued(function)) return true; |
73 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 120 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
74 isolate_, tracer_.get(), function, max_stack_size_)); | 121 isolate_, tracer_.get(), function, max_stack_size_)); |
75 std::pair<int, int> key(Script::cast(function->script())->id(), | 122 std::pair<int, int> key(Script::cast(function->script())->id(), |
76 function->function_literal_id()); | 123 function->function_literal_id()); |
77 jobs_.insert(std::make_pair(key, std::move(job))); | 124 JobMap::const_iterator it = jobs_.insert(std::make_pair(key, std::move(job))); |
125 ClassifyJob(it); | |
126 ScheduleIdleTaskIfNeeded(); | |
78 return true; | 127 return true; |
79 } | 128 } |
80 | 129 |
81 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 130 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
82 return GetJobFor(function) != jobs_.end(); | 131 return GetJobFor(function) != jobs_.end(); |
83 } | 132 } |
84 | 133 |
85 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 134 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
86 JobMap::const_iterator job = GetJobFor(function); | 135 JobMap::const_iterator job = GetJobFor(function); |
87 CHECK(job != jobs_.end()); | 136 CHECK(job != jobs_.end()); |
88 | 137 |
89 // TODO(jochen): Check if there's an in-flight background task working on this | 138 // TODO(jochen): Check if there's an in-flight background task working on this |
90 // job. | 139 // job. |
91 while (!IsFinished(job->second.get())) { | 140 while (!IsFinished(job->second.get())) { |
92 DoNextStepOnMainThread(job->second.get()); | 141 DoNextStepOnMainThread(isolate_, job->second.get(), |
142 ExceptionHandling::kKeep); | |
93 } | 143 } |
94 bool result = job->second->status() != CompileJobStatus::kFailed; | 144 bool result = job->second->status() != CompileJobStatus::kFailed; |
145 job->second->ResetOnMainThread(); | |
146 jobs_for_background_thread_.erase(job); | |
147 jobs_for_main_thread_.erase(job); | |
95 jobs_.erase(job); | 148 jobs_.erase(job); |
96 return result; | 149 return result; |
97 } | 150 } |
98 | 151 |
99 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, | 152 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
100 BlockingBehavior blocking) { | 153 BlockingBehavior blocking) { |
101 USE(blocking); | 154 USE(blocking); |
102 JobMap::const_iterator job = GetJobFor(function); | 155 JobMap::const_iterator job = GetJobFor(function); |
103 CHECK(job != jobs_.end()); | 156 CHECK(job != jobs_.end()); |
104 | 157 |
105 // TODO(jochen): Check if there's an in-flight background task working on this | 158 // TODO(jochen): Check if there's an in-flight background task working on this |
106 // job. | 159 // job. |
160 job->second->ResetOnMainThread(); | |
161 jobs_for_background_thread_.erase(job); | |
162 jobs_for_main_thread_.erase(job); | |
107 jobs_.erase(job); | 163 jobs_.erase(job); |
108 } | 164 } |
109 | 165 |
110 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 166 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
111 USE(blocking); | 167 USE(blocking); |
112 // TODO(jochen): Check if there's an in-flight background task working on this | 168 // TODO(jochen): Check if there's an in-flight background task working on this |
113 // job. | 169 // job. |
170 for (auto& kv : jobs_) { | |
171 kv.second->ResetOnMainThread(); | |
172 } | |
173 jobs_for_background_thread_.clear(); | |
174 jobs_for_main_thread_.clear(); | |
114 jobs_.clear(); | 175 jobs_.clear(); |
115 } | 176 } |
116 | 177 |
117 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 178 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
118 Handle<SharedFunctionInfo> shared) const { | 179 Handle<SharedFunctionInfo> shared) const { |
119 if (!shared->script()->IsScript()) return jobs_.end(); | 180 if (!shared->script()->IsScript()) return jobs_.end(); |
120 std::pair<int, int> key(Script::cast(shared->script())->id(), | 181 std::pair<int, int> key(Script::cast(shared->script())->id(), |
121 shared->function_literal_id()); | 182 shared->function_literal_id()); |
122 auto range = jobs_.equal_range(key); | 183 auto range = jobs_.equal_range(key); |
123 for (auto job = range.first; job != range.second; ++job) { | 184 for (auto job = range.first; job != range.second; ++job) { |
124 if (job->second->IsAssociatedWith(shared)) return job; | 185 if (job->second->IsAssociatedWith(shared)) return job; |
125 } | 186 } |
126 return jobs_.end(); | 187 return jobs_.end(); |
127 } | 188 } |
128 | 189 |
190 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
191 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |
192 if (!platform_->IdleTasksEnabled(v8_isolate)) return; | |
193 if (idle_task_scheduled_) return; | |
194 if (jobs_.empty()) return; | |
195 idle_task_scheduled_ = true; | |
196 platform_->CallIdleOnForegroundThread(v8_isolate, | |
197 new IdleTask(isolate_, this)); | |
198 } | |
199 | |
200 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | |
201 idle_task_scheduled_ = false; | |
202 | |
203 DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_main_thread_); | |
204 if (deadline_in_seconds <= platform_->MonotonicallyIncreasingTime()) return; | |
205 DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_background_thread_); | |
206 } | |
207 | |
208 void CompilerDispatcher::DoIdleWorkForJobSet( | |
marja
2016/12/15 15:56:43
Can you add some high-level comments to CompilerDi
| |
209 double deadline_in_seconds, | |
210 std::unordered_set<JobMap::const_iterator, JobIteratorHash>* job_set) { | |
211 bool work_left = false; | |
212 | |
213 for (auto it = job_set->begin(); it != job_set->end();) { | |
214 double idle_time_in_seconds = | |
215 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | |
216 if (idle_time_in_seconds <= 0.0) { | |
217 work_left = true; | |
218 break; | |
219 } | |
220 | |
221 auto job = (*it); | |
222 // We'll put the job back on the right queue if we don't manage to process | |
223 // it completely. | |
224 it = jobs_for_main_thread_.erase(it); | |
marja
2016/12/15 15:56:43
How is this supposed to work? it is an iterator fo
jochen (gone - plz use gerrit)
2016/12/15 20:18:44
erase() in C++11 returns the next iterator, it's s
| |
225 | |
226 // Advance the current job as much as possible. | |
227 for (;;) { | |
228 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | |
229 if (idle_time_in_seconds < | |
230 (estimate_in_ms / | |
231 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | |
232 // If there's not enough time left, try to estimate whether we would | |
233 // have managed to finish the job in a large idle task to assess | |
234 // whether we should ask for another idle callback. | |
235 if (estimate_in_ms <= kMaxIdleTimeToExpectInMs) work_left = true; | |
236 // As there's remaining work, put the job back on the queue. | |
237 ClassifyJob(job); | |
marja
2016/12/15 15:56:43
As job_set is a pointer to one of the underlying u
marja
2016/12/15 15:56:43
The interaction between the queues and the job set
jochen (gone - plz use gerrit)
2016/12/15 20:18:44
once we also have a background scheduler, they'll
| |
238 break; | |
239 } | |
240 DoNextStepOnMainThread(isolate_, job->second.get(), | |
241 ExceptionHandling::kSwallow); | |
242 | |
243 if (IsFinished(job->second.get())) { | |
244 job->second->ResetOnMainThread(); | |
245 jobs_.erase(job); | |
marja
2016/12/15 15:56:43
There's jobs_ and job_set, could you name them som
| |
246 break; | |
247 } | |
248 | |
249 idle_time_in_seconds = | |
250 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | |
251 } | |
252 } | |
253 if (work_left) ScheduleIdleTaskIfNeeded(); | |
vogelheim
2016/12/15 15:24:12
This would potentially be called twice, since DoId
vogelheim
2016/12/15 15:24:12
At this point, wouldn't (work_left == !job_set.emp
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
I found it more readable to put it here, instead o
| |
254 } | |
255 | |
256 void CompilerDispatcher::ClassifyJob(JobMap::const_iterator it) { | |
marja
2016/12/15 15:56:43
This function name is misleading, it doesn't just
| |
257 bool can_go_on_background_thread = false; | |
258 CompilerDispatcherJob* job = it->second.get(); | |
259 if (job->status() == CompileJobStatus::kReadyToParse && | |
260 job->can_parse_on_background_thread()) { | |
261 can_go_on_background_thread = true; | |
262 } else if (job->status() == CompileJobStatus::kReadyToCompile && | |
263 job->can_compile_on_background_thread()) { | |
264 can_go_on_background_thread = true; | |
265 } | |
vogelheim
2016/12/15 15:24:11
style nitpick; feel free to ignore... The if-else
jochen (gone - plz use gerrit)
2016/12/15 15:37:59
done
| |
266 | |
267 if (can_go_on_background_thread) { | |
268 jobs_for_background_thread_.insert(it); | |
269 } else { | |
270 jobs_for_main_thread_.insert(it); | |
271 } | |
272 } | |
273 | |
129 } // namespace internal | 274 } // namespace internal |
130 } // namespace v8 | 275 } // namespace v8 |
OLD | NEW |