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 "include/v8.h" | 8 #include "include/v8.h" |
9 #include "src/base/platform/time.h" | 9 #include "src/base/platform/time.h" |
10 #include "src/cancelable-task.h" | 10 #include "src/cancelable-task.h" |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
59 try_catch.ReThrow(); | 59 try_catch.ReThrow(); |
60 } | 60 } |
61 return job->status() != CompileJobStatus::kFailed; | 61 return job->status() != CompileJobStatus::kFailed; |
62 } | 62 } |
63 | 63 |
64 bool IsFinished(CompilerDispatcherJob* job) { | 64 bool IsFinished(CompilerDispatcherJob* job) { |
65 return job->status() == CompileJobStatus::kDone || | 65 return job->status() == CompileJobStatus::kDone || |
66 job->status() == CompileJobStatus::kFailed; | 66 job->status() == CompileJobStatus::kFailed; |
67 } | 67 } |
68 | 68 |
69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { | |
70 return (job->status() == CompileJobStatus::kReadyToParse && | |
71 job->can_parse_on_background_thread()) || | |
72 (job->status() == CompileJobStatus::kReadyToCompile && | |
73 job->can_compile_on_background_thread()); | |
74 } | |
75 | |
76 void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) { | |
77 DCHECK(CanRunOnAnyThread(job)); | |
78 switch (job->status()) { | |
79 case CompileJobStatus::kReadyToParse: | |
80 job->Parse(); | |
81 break; | |
82 | |
83 case CompileJobStatus::kReadyToCompile: | |
84 job->Compile(); | |
85 break; | |
86 | |
87 default: | |
88 UNREACHABLE(); | |
89 } | |
90 } | |
91 | |
69 // Theoretically we get 50ms of idle time max, however it's unlikely that | 92 // Theoretically we get 50ms of idle time max, however it's unlikely that |
70 // we'll get all of it so try to be a conservative. | 93 // we'll get all of it so try to be a conservative. |
71 const double kMaxIdleTimeToExpectInMs = 40; | 94 const double kMaxIdleTimeToExpectInMs = 40; |
72 | 95 |
73 } // namespace | 96 } // namespace |
74 | 97 |
98 class CompilerDispatcher::BackgroundTask : public CancelableTask { | |
99 public: | |
100 BackgroundTask(Isolate* isolate, CompilerDispatcher* dispatcher); | |
101 ~BackgroundTask() override; | |
102 | |
103 // CancelableTask implementation. | |
104 void RunInternal() override; | |
105 | |
106 private: | |
107 CompilerDispatcher* dispatcher_; | |
108 | |
109 DISALLOW_COPY_AND_ASSIGN(BackgroundTask); | |
110 }; | |
111 | |
112 CompilerDispatcher::BackgroundTask::BackgroundTask( | |
113 Isolate* isolate, CompilerDispatcher* dispatcher) | |
114 : CancelableTask(isolate), dispatcher_(dispatcher) {} | |
115 | |
116 CompilerDispatcher::BackgroundTask::~BackgroundTask() {} | |
117 | |
118 void CompilerDispatcher::BackgroundTask::RunInternal() { | |
119 dispatcher_->DoBackgroundWork(); | |
120 } | |
121 | |
75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { | 122 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
76 public: | 123 public: |
77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); | 124 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
78 ~IdleTask() override; | 125 ~IdleTask() override; |
79 | 126 |
80 // CancelableIdleTask implementation. | 127 // CancelableIdleTask implementation. |
81 void RunInternal(double deadline_in_seconds) override; | 128 void RunInternal(double deadline_in_seconds) override; |
82 | 129 |
83 private: | 130 private: |
84 CompilerDispatcher* dispatcher_; | 131 CompilerDispatcher* dispatcher_; |
(...skipping 10 matching lines...) Expand all Loading... | |
95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | 142 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { |
96 dispatcher_->DoIdleWork(deadline_in_seconds); | 143 dispatcher_->DoIdleWork(deadline_in_seconds); |
97 } | 144 } |
98 | 145 |
99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 146 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
100 size_t max_stack_size) | 147 size_t max_stack_size) |
101 : isolate_(isolate), | 148 : isolate_(isolate), |
102 platform_(platform), | 149 platform_(platform), |
103 max_stack_size_(max_stack_size), | 150 max_stack_size_(max_stack_size), |
104 tracer_(new CompilerDispatcherTracer(isolate_)), | 151 tracer_(new CompilerDispatcherTracer(isolate_)), |
105 idle_task_scheduled_(false) {} | 152 idle_task_scheduled_(false), |
153 num_scheduled_background_tasks_(0), | |
154 main_thread_blocking_on_job_(nullptr) {} | |
106 | 155 |
107 CompilerDispatcher::~CompilerDispatcher() { | 156 CompilerDispatcher::~CompilerDispatcher() { |
108 // To avoid crashing in unit tests due to unfished jobs. | 157 // To avoid crashing in unit tests due to unfished jobs. |
109 AbortAll(BlockingBehavior::kBlock); | 158 AbortAll(BlockingBehavior::kBlock); |
110 } | 159 } |
111 | 160 |
112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 161 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
113 if (!IsEnabled()) return false; | 162 if (!IsEnabled()) return false; |
114 | 163 |
115 // We only handle functions (no eval / top-level code / wasm) that are | 164 // We only handle functions (no eval / top-level code / wasm) that are |
(...skipping 15 matching lines...) Expand all Loading... | |
131 | 180 |
132 bool CompilerDispatcher::IsEnabled() const { | 181 bool CompilerDispatcher::IsEnabled() const { |
133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 182 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); | 183 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); |
135 } | 184 } |
136 | 185 |
137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 186 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
138 return GetJobFor(function) != jobs_.end(); | 187 return GetJobFor(function) != jobs_.end(); |
139 } | 188 } |
140 | 189 |
190 void CompilerDispatcher::EnsureNotOnBackground(CompilerDispatcherJob* job) { | |
marja
2017/01/03 10:42:41
This function name is confusing. I had to read it
jochen (gone - plz use gerrit)
2017/01/03 12:59:35
done
| |
191 base::LockGuard<base::Mutex> lock(&mutex_); | |
192 if (running_background_jobs_.find(job) == running_background_jobs_.end()) { | |
193 pending_background_jobs_.erase(job); | |
194 return; | |
195 } | |
196 DCHECK_NULL(main_thread_blocking_on_job_); | |
197 main_thread_blocking_on_job_ = job; | |
198 while (main_thread_blocking_on_job_ != nullptr) { | |
199 main_thread_blocking_signal_.Wait(&mutex_); | |
200 } | |
201 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); | |
vogelheim
2017/01/03 10:32:37
Also the same DCHECK(...), with running_background
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
done
| |
202 } | |
203 | |
141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 204 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
142 JobMap::const_iterator job = GetJobFor(function); | 205 JobMap::const_iterator job = GetJobFor(function); |
143 CHECK(job != jobs_.end()); | 206 CHECK(job != jobs_.end()); |
144 | 207 |
145 // TODO(jochen): Check if there's an in-flight background task working on this | 208 EnsureNotOnBackground(job->second.get()); |
146 // job. | |
147 while (!IsFinished(job->second.get())) { | 209 while (!IsFinished(job->second.get())) { |
148 DoNextStepOnMainThread(isolate_, job->second.get(), | 210 DoNextStepOnMainThread(isolate_, job->second.get(), |
149 ExceptionHandling::kThrow); | 211 ExceptionHandling::kThrow); |
150 } | 212 } |
151 bool result = job->second->status() != CompileJobStatus::kFailed; | 213 bool result = job->second->status() != CompileJobStatus::kFailed; |
152 job->second->ResetOnMainThread(); | 214 job->second->ResetOnMainThread(); |
153 jobs_.erase(job); | 215 jobs_.erase(job); |
154 return result; | 216 return result; |
155 } | 217 } |
156 | 218 |
157 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, | |
158 BlockingBehavior blocking) { | |
159 USE(blocking); | |
160 JobMap::const_iterator job = GetJobFor(function); | |
161 CHECK(job != jobs_.end()); | |
162 | |
163 // TODO(jochen): Check if there's an in-flight background task working on this | |
164 // job. | |
165 job->second->ResetOnMainThread(); | |
166 jobs_.erase(job); | |
167 } | |
168 | |
169 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 219 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
170 USE(blocking); | 220 // TODO(jochen): Implement support for non-blocking abort. |
171 // TODO(jochen): Check if there's an in-flight background task working on this | 221 DCHECK(blocking == BlockingBehavior::kBlock); |
vogelheim
2017/01/03 10:32:37
DCHECK_EQ ?
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
doesn't work with enum classes
| |
172 // job. | |
173 for (auto& kv : jobs_) { | 222 for (auto& kv : jobs_) { |
223 EnsureNotOnBackground(kv.second.get()); | |
174 kv.second->ResetOnMainThread(); | 224 kv.second->ResetOnMainThread(); |
175 } | 225 } |
176 jobs_.clear(); | 226 jobs_.clear(); |
177 } | 227 } |
178 | 228 |
179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 229 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
180 Handle<SharedFunctionInfo> shared) const { | 230 Handle<SharedFunctionInfo> shared) const { |
181 if (!shared->script()->IsScript()) return jobs_.end(); | 231 if (!shared->script()->IsScript()) return jobs_.end(); |
182 std::pair<int, int> key(Script::cast(shared->script())->id(), | 232 std::pair<int, int> key(Script::cast(shared->script())->id(), |
183 shared->function_literal_id()); | 233 shared->function_literal_id()); |
184 auto range = jobs_.equal_range(key); | 234 auto range = jobs_.equal_range(key); |
185 for (auto job = range.first; job != range.second; ++job) { | 235 for (auto job = range.first; job != range.second; ++job) { |
186 if (job->second->IsAssociatedWith(shared)) return job; | 236 if (job->second->IsAssociatedWith(shared)) return job; |
187 } | 237 } |
188 return jobs_.end(); | 238 return jobs_.end(); |
189 } | 239 } |
190 | 240 |
191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | 241 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { |
192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 242 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); | 243 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); |
194 if (idle_task_scheduled_) return; | 244 { |
195 if (jobs_.empty()) return; | 245 base::LockGuard<base::Mutex> lock(&mutex_); |
196 idle_task_scheduled_ = true; | 246 if (idle_task_scheduled_) return; |
247 idle_task_scheduled_ = true; | |
248 } | |
197 platform_->CallIdleOnForegroundThread(v8_isolate, | 249 platform_->CallIdleOnForegroundThread(v8_isolate, |
198 new IdleTask(isolate_, this)); | 250 new IdleTask(isolate_, this)); |
199 } | 251 } |
200 | 252 |
253 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
254 if (jobs_.empty()) return; | |
255 ScheduleIdleTaskFromAnyThread(); | |
256 } | |
257 | |
258 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( | |
259 CompilerDispatcherJob* job) { | |
260 if (!CanRunOnAnyThread(job)) return; | |
261 { | |
262 base::LockGuard<base::Mutex> lock(&mutex_); | |
263 pending_background_jobs_.insert(job); | |
264 } | |
265 ScheduleMoreBackgroundTasksIfNeeded(); | |
266 } | |
267 | |
268 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { | |
269 if (FLAG_single_threaded) return; | |
vogelheim
2017/01/03 10:32:37
Hmm. So if --single_threaded, ConsiderJobForBackgr
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
if the job is too big for idle time. Since the DoI
| |
270 { | |
271 base::LockGuard<base::Mutex> lock(&mutex_); | |
272 if (pending_background_jobs_.empty()) return; | |
273 if (platform_->NumberOfAvailableBackgroundThreads() <= | |
274 num_scheduled_background_tasks_) { | |
275 return; | |
276 } | |
277 ++num_scheduled_background_tasks_; | |
278 } | |
279 platform_->CallOnBackgroundThread(new BackgroundTask(isolate_, this), | |
280 v8::Platform::kShortRunningTask); | |
281 } | |
282 | |
283 void CompilerDispatcher::DoBackgroundWork() { | |
284 CompilerDispatcherJob* job = nullptr; | |
marja
2017/01/03 10:43:41
Can you add an assert (somewhere, maybe here) that
jochen (gone - plz use gerrit)
2017/01/03 12:59:35
that assert will not necessarily hold: the embedde
marja
2017/01/03 13:14:57
Hmm, I'm missing something...
ScheduleMoreBackgro
jochen (gone - plz use gerrit)
2017/01/03 14:01:44
assume that each task counts down num_scheduled_ba
marja
2017/01/03 14:22:45
Ah I see
It's slightly unintuitive though that nu
| |
285 { | |
286 base::LockGuard<base::Mutex> lock(&mutex_); | |
287 --num_scheduled_background_tasks_; | |
288 if (!pending_background_jobs_.empty()) { | |
289 auto it = pending_background_jobs_.begin(); | |
290 job = *it; | |
291 pending_background_jobs_.erase(it); | |
292 running_background_jobs_.insert(job); | |
293 } | |
294 } | |
295 if (job == nullptr) return; | |
296 DoNextStepOnBackgroundThread(job); | |
297 { | |
298 base::LockGuard<base::Mutex> lock(&mutex_); | |
299 running_background_jobs_.erase(job); | |
300 | |
301 if (main_thread_blocking_on_job_ == job) { | |
302 main_thread_blocking_on_job_ = nullptr; | |
303 main_thread_blocking_signal_.NotifyOne(); | |
304 } | |
305 } | |
306 ScheduleMoreBackgroundTasksIfNeeded(); | |
307 ScheduleIdleTaskFromAnyThread(); | |
vogelheim
2017/01/03 10:32:37
ScheduleIdleTaskFromAnyThread unconditionally sche
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
correct. Added a comment
| |
308 } | |
309 | |
201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 310 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
202 idle_task_scheduled_ = false; | 311 { |
312 base::LockGuard<base::Mutex> lock(&mutex_); | |
313 idle_task_scheduled_ = false; | |
314 } | |
203 | 315 |
204 // Number of jobs that are unlikely to make progress during any idle callback | 316 // Number of jobs that are unlikely to make progress during any idle callback |
205 // due to their estimated duration. | 317 // due to their estimated duration. |
206 size_t too_long_jobs = 0; | 318 size_t too_long_jobs = 0; |
207 | 319 |
208 // Iterate over all available jobs & remaining time. For each job, decide | 320 // Iterate over all available jobs & remaining time. For each job, decide |
209 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 321 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
210 // finished), or 3) make progress on it. | 322 // finished), or 3) make progress on it. |
211 double idle_time_in_seconds = | 323 double idle_time_in_seconds = |
212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | 324 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
213 for (auto job = jobs_.begin(); | 325 for (auto job = jobs_.begin(); |
214 job != jobs_.end() && idle_time_in_seconds > 0.0; | 326 job != jobs_.end() && idle_time_in_seconds > 0.0; |
215 idle_time_in_seconds = | 327 idle_time_in_seconds = |
216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | 328 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { |
329 { | |
330 // Don't work on jobs that are being worked on by background tasks. | |
331 // Similarly, remove jobs we work on from the set of available background | |
332 // jobs. | |
333 base::LockGuard<base::Mutex> lock(&mutex_); | |
334 if (running_background_jobs_.find(job->second.get()) != | |
335 running_background_jobs_.end()) { | |
336 ++job; | |
337 continue; | |
338 } | |
339 auto it = pending_background_jobs_.find(job->second.get()); | |
marja
2017/01/03 10:42:41
This is kinda weird... here we erase the job from
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
we're always working on it in the sense that we in
marja
2017/01/03 13:14:57
If the job is too big (or the amount of idle time
jochen (gone - plz use gerrit)
2017/01/03 14:01:44
what about this?
| |
340 if (it != pending_background_jobs_.end()) { | |
341 pending_background_jobs_.erase(it); | |
342 } | |
343 } | |
217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | 344 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
218 if (idle_time_in_seconds < | 345 if (idle_time_in_seconds < |
219 (estimate_in_ms / | 346 (estimate_in_ms / |
220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | 347 static_cast<double>(base::Time::kMillisecondsPerSecond))) { |
221 // If there's not enough time left, try to estimate whether we would | 348 // If there's not enough time left, try to estimate whether we would |
222 // have managed to finish the job in a large idle task to assess | 349 // have managed to finish the job in a large idle task to assess |
223 // whether we should ask for another idle callback. | 350 // whether we should ask for another idle callback. |
224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | 351 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; |
352 ConsiderJobForBackgroundProcessing(job->second.get()); | |
marja
2017/01/03 10:42:40
This is somewhat surprising... so we only push tho
jochen (gone - plz use gerrit)
2017/01/03 12:59:36
the other cases are:
- the job is finished (no ne
marja
2017/01/03 13:14:57
But if there are a zillion of small tasks and seve
jochen (gone - plz use gerrit)
2017/01/03 14:01:44
added a lengthy comment
| |
225 ++job; | 353 ++job; |
226 } else if (IsFinished(job->second.get())) { | 354 } else if (IsFinished(job->second.get())) { |
227 job->second->ResetOnMainThread(); | 355 job->second->ResetOnMainThread(); |
228 job = jobs_.erase(job); | 356 job = jobs_.erase(job); |
229 break; | 357 break; |
230 } else { | 358 } else { |
231 // Do one step, and keep processing the job (as we don't advance the | 359 // Do one step, and keep processing the job (as we don't advance the |
232 // iterator). | 360 // iterator). |
233 DoNextStepOnMainThread(isolate_, job->second.get(), | 361 DoNextStepOnMainThread(isolate_, job->second.get(), |
234 ExceptionHandling::kSwallow); | 362 ExceptionHandling::kSwallow); |
235 } | 363 } |
236 } | 364 } |
237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 365 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
238 } | 366 } |
239 | 367 |
240 } // namespace internal | 368 } // namespace internal |
241 } // namespace v8 | 369 } // namespace v8 |
OLD | NEW |