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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 | 45 |
46 case CompileJobStatus::kCompiled: | 46 case CompileJobStatus::kCompiled: |
47 job->FinalizeCompilingOnMainThread(); | 47 job->FinalizeCompilingOnMainThread(); |
48 break; | 48 break; |
49 | 49 |
50 case CompileJobStatus::kFailed: | 50 case CompileJobStatus::kFailed: |
51 case CompileJobStatus::kDone: | 51 case CompileJobStatus::kDone: |
52 break; | 52 break; |
53 } | 53 } |
54 | 54 |
55 if (job->status() == CompileJobStatus::kFailed) { | 55 DCHECK_EQ(job->status() == CompileJobStatus::kFailed, |
56 DCHECK(isolate->has_pending_exception()); | 56 isolate->has_pending_exception()); |
57 if (exception_handling == ExceptionHandling::kSwallow) { | 57 if (job->status() == CompileJobStatus::kFailed && |
58 isolate->clear_pending_exception(); | 58 exception_handling == ExceptionHandling::kSwallow) { |
59 } | 59 isolate->clear_pending_exception(); |
60 } else { | |
61 DCHECK(!isolate->has_pending_exception()); | |
62 } | 60 } |
63 return job->status() != CompileJobStatus::kFailed; | 61 return job->status() != CompileJobStatus::kFailed; |
64 } | 62 } |
65 | 63 |
66 bool IsFinished(CompilerDispatcherJob* job) { | 64 bool IsFinished(CompilerDispatcherJob* job) { |
67 return job->status() == CompileJobStatus::kDone || | 65 return job->status() == CompileJobStatus::kDone || |
68 job->status() == CompileJobStatus::kFailed; | 66 job->status() == CompileJobStatus::kFailed; |
69 } | 67 } |
70 | 68 |
71 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { | 69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) { |
(...skipping 18 matching lines...) Expand all Loading... |
90 UNREACHABLE(); | 88 UNREACHABLE(); |
91 } | 89 } |
92 } | 90 } |
93 | 91 |
94 // 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 |
95 // 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. |
96 const double kMaxIdleTimeToExpectInMs = 40; | 94 const double kMaxIdleTimeToExpectInMs = 40; |
97 | 95 |
98 } // namespace | 96 } // namespace |
99 | 97 |
| 98 class CompilerDispatcher::AbortTask : public CancelableTask { |
| 99 public: |
| 100 AbortTask(Isolate* isolate, CancelableTaskManager* task_manager, |
| 101 CompilerDispatcher* dispatcher); |
| 102 ~AbortTask() override; |
| 103 |
| 104 // CancelableTask implementation. |
| 105 void RunInternal() override; |
| 106 |
| 107 private: |
| 108 CompilerDispatcher* dispatcher_; |
| 109 |
| 110 DISALLOW_COPY_AND_ASSIGN(AbortTask); |
| 111 }; |
| 112 |
| 113 CompilerDispatcher::AbortTask::AbortTask(Isolate* isolate, |
| 114 CancelableTaskManager* task_manager, |
| 115 CompilerDispatcher* dispatcher) |
| 116 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {} |
| 117 |
| 118 CompilerDispatcher::AbortTask::~AbortTask() {} |
| 119 |
| 120 void CompilerDispatcher::AbortTask::RunInternal() { |
| 121 dispatcher_->AbortInactiveJobs(); |
| 122 } |
| 123 |
100 class CompilerDispatcher::BackgroundTask : public CancelableTask { | 124 class CompilerDispatcher::BackgroundTask : public CancelableTask { |
101 public: | 125 public: |
102 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, | 126 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, |
103 CompilerDispatcher* dispatcher); | 127 CompilerDispatcher* dispatcher); |
104 ~BackgroundTask() override; | 128 ~BackgroundTask() override; |
105 | 129 |
106 // CancelableTask implementation. | 130 // CancelableTask implementation. |
107 void RunInternal() override; | 131 void RunInternal() override; |
108 | 132 |
109 private: | 133 private: |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 dispatcher_->DoIdleWork(deadline_in_seconds); | 173 dispatcher_->DoIdleWork(deadline_in_seconds); |
150 } | 174 } |
151 | 175 |
152 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 176 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
153 size_t max_stack_size) | 177 size_t max_stack_size) |
154 : isolate_(isolate), | 178 : isolate_(isolate), |
155 platform_(platform), | 179 platform_(platform), |
156 max_stack_size_(max_stack_size), | 180 max_stack_size_(max_stack_size), |
157 tracer_(new CompilerDispatcherTracer(isolate_)), | 181 tracer_(new CompilerDispatcherTracer(isolate_)), |
158 task_manager_(new CancelableTaskManager()), | 182 task_manager_(new CancelableTaskManager()), |
| 183 abort_(false), |
159 idle_task_scheduled_(false), | 184 idle_task_scheduled_(false), |
160 num_scheduled_background_tasks_(0), | 185 num_scheduled_background_tasks_(0), |
161 main_thread_blocking_on_job_(nullptr) {} | 186 main_thread_blocking_on_job_(nullptr), |
| 187 block_for_testing_(false), |
| 188 semaphore_for_testing_(0) {} |
162 | 189 |
163 CompilerDispatcher::~CompilerDispatcher() { | 190 CompilerDispatcher::~CompilerDispatcher() { |
164 // To avoid crashing in unit tests due to unfished jobs. | 191 // To avoid crashing in unit tests due to unfished jobs. |
165 AbortAll(BlockingBehavior::kBlock); | 192 AbortAll(BlockingBehavior::kBlock); |
166 task_manager_->CancelAndWait(); | 193 task_manager_->CancelAndWait(); |
167 } | 194 } |
168 | 195 |
169 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 196 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
170 if (!IsEnabled()) return false; | 197 if (!IsEnabled()) return false; |
171 | 198 |
| 199 { |
| 200 base::LockGuard<base::Mutex> lock(&mutex_); |
| 201 if (abort_) return false; |
| 202 } |
| 203 |
172 // We only handle functions (no eval / top-level code / wasm) that are | 204 // We only handle functions (no eval / top-level code / wasm) that are |
173 // attached to a script. | 205 // attached to a script. |
174 if (!function->script()->IsScript() || !function->is_function() || | 206 if (!function->script()->IsScript() || !function->is_function() || |
175 function->asm_function() || function->native()) { | 207 function->asm_function() || function->native()) { |
176 return false; | 208 return false; |
177 } | 209 } |
178 | 210 |
179 if (IsEnqueued(function)) return true; | 211 if (IsEnqueued(function)) return true; |
180 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 212 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
181 isolate_, tracer_.get(), function, max_stack_size_)); | 213 isolate_, tracer_.get(), function, max_stack_size_)); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 CHECK(job != jobs_.end()); | 248 CHECK(job != jobs_.end()); |
217 | 249 |
218 WaitForJobIfRunningOnBackground(job->second.get()); | 250 WaitForJobIfRunningOnBackground(job->second.get()); |
219 while (!IsFinished(job->second.get())) { | 251 while (!IsFinished(job->second.get())) { |
220 DoNextStepOnMainThread(isolate_, job->second.get(), | 252 DoNextStepOnMainThread(isolate_, job->second.get(), |
221 ExceptionHandling::kThrow); | 253 ExceptionHandling::kThrow); |
222 } | 254 } |
223 bool result = job->second->status() != CompileJobStatus::kFailed; | 255 bool result = job->second->status() != CompileJobStatus::kFailed; |
224 job->second->ResetOnMainThread(); | 256 job->second->ResetOnMainThread(); |
225 jobs_.erase(job); | 257 jobs_.erase(job); |
| 258 if (jobs_.empty()) { |
| 259 base::LockGuard<base::Mutex> lock(&mutex_); |
| 260 abort_ = false; |
| 261 } |
226 return result; | 262 return result; |
227 } | 263 } |
228 | 264 |
229 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 265 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
230 // TODO(jochen): Implement support for non-blocking abort. | 266 bool background_tasks_running = |
231 DCHECK(blocking == BlockingBehavior::kBlock); | 267 task_manager_->TryAbortAll() == CancelableTaskManager::kTaskRunning; |
232 for (auto& kv : jobs_) { | 268 if (!background_tasks_running || blocking == BlockingBehavior::kBlock) { |
233 WaitForJobIfRunningOnBackground(kv.second.get()); | 269 for (auto& it : jobs_) { |
234 kv.second->ResetOnMainThread(); | 270 WaitForJobIfRunningOnBackground(it.second.get()); |
| 271 it.second->ResetOnMainThread(); |
| 272 } |
| 273 jobs_.clear(); |
| 274 { |
| 275 base::LockGuard<base::Mutex> lock(&mutex_); |
| 276 DCHECK(pending_background_jobs_.empty()); |
| 277 DCHECK(running_background_jobs_.empty()); |
| 278 abort_ = false; |
| 279 } |
| 280 return; |
235 } | 281 } |
236 jobs_.clear(); | 282 |
| 283 { |
| 284 base::LockGuard<base::Mutex> lock(&mutex_); |
| 285 abort_ = true; |
| 286 pending_background_jobs_.clear(); |
| 287 } |
| 288 AbortInactiveJobs(); |
| 289 |
| 290 // All running background jobs might already have scheduled idle tasks instead |
| 291 // of abort tasks. Schedule a single abort task here to make sure they get |
| 292 // processed as soon as possible (and not first when we have idle time). |
| 293 ScheduleAbortTask(); |
| 294 } |
| 295 |
| 296 void CompilerDispatcher::AbortInactiveJobs() { |
| 297 { |
| 298 base::LockGuard<base::Mutex> lock(&mutex_); |
| 299 // Since we schedule two abort tasks per async abort, we might end up |
| 300 // here with nothing left to do. |
| 301 if (!abort_) return; |
| 302 } |
| 303 for (auto it = jobs_.begin(); it != jobs_.end();) { |
| 304 auto job = it; |
| 305 ++it; |
| 306 { |
| 307 base::LockGuard<base::Mutex> lock(&mutex_); |
| 308 if (running_background_jobs_.find(job->second.get()) != |
| 309 running_background_jobs_.end()) { |
| 310 continue; |
| 311 } |
| 312 } |
| 313 job->second->ResetOnMainThread(); |
| 314 jobs_.erase(job); |
| 315 } |
| 316 if (jobs_.empty()) { |
| 317 base::LockGuard<base::Mutex> lock(&mutex_); |
| 318 abort_ = false; |
| 319 } |
237 } | 320 } |
238 | 321 |
239 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 322 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
240 Handle<SharedFunctionInfo> shared) const { | 323 Handle<SharedFunctionInfo> shared) const { |
241 if (!shared->script()->IsScript()) return jobs_.end(); | 324 if (!shared->script()->IsScript()) return jobs_.end(); |
242 std::pair<int, int> key(Script::cast(shared->script())->id(), | 325 std::pair<int, int> key(Script::cast(shared->script())->id(), |
243 shared->function_literal_id()); | 326 shared->function_literal_id()); |
244 auto range = jobs_.equal_range(key); | 327 auto range = jobs_.equal_range(key); |
245 for (auto job = range.first; job != range.second; ++job) { | 328 for (auto job = range.first; job != range.second; ++job) { |
246 if (job->second->IsAssociatedWith(shared)) return job; | 329 if (job->second->IsAssociatedWith(shared)) return job; |
(...skipping 11 matching lines...) Expand all Loading... |
258 } | 341 } |
259 platform_->CallIdleOnForegroundThread( | 342 platform_->CallIdleOnForegroundThread( |
260 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); | 343 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); |
261 } | 344 } |
262 | 345 |
263 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | 346 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
264 if (jobs_.empty()) return; | 347 if (jobs_.empty()) return; |
265 ScheduleIdleTaskFromAnyThread(); | 348 ScheduleIdleTaskFromAnyThread(); |
266 } | 349 } |
267 | 350 |
| 351 void CompilerDispatcher::ScheduleAbortTask() { |
| 352 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
| 353 platform_->CallOnForegroundThread( |
| 354 v8_isolate, new AbortTask(isolate_, task_manager_.get(), this)); |
| 355 } |
| 356 |
268 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( | 357 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( |
269 CompilerDispatcherJob* job) { | 358 CompilerDispatcherJob* job) { |
270 if (!CanRunOnAnyThread(job)) return; | 359 if (!CanRunOnAnyThread(job)) return; |
271 { | 360 { |
272 base::LockGuard<base::Mutex> lock(&mutex_); | 361 base::LockGuard<base::Mutex> lock(&mutex_); |
273 pending_background_jobs_.insert(job); | 362 pending_background_jobs_.insert(job); |
274 } | 363 } |
275 ScheduleMoreBackgroundTasksIfNeeded(); | 364 ScheduleMoreBackgroundTasksIfNeeded(); |
276 } | 365 } |
277 | 366 |
(...skipping 19 matching lines...) Expand all Loading... |
297 base::LockGuard<base::Mutex> lock(&mutex_); | 386 base::LockGuard<base::Mutex> lock(&mutex_); |
298 --num_scheduled_background_tasks_; | 387 --num_scheduled_background_tasks_; |
299 if (!pending_background_jobs_.empty()) { | 388 if (!pending_background_jobs_.empty()) { |
300 auto it = pending_background_jobs_.begin(); | 389 auto it = pending_background_jobs_.begin(); |
301 job = *it; | 390 job = *it; |
302 pending_background_jobs_.erase(it); | 391 pending_background_jobs_.erase(it); |
303 running_background_jobs_.insert(job); | 392 running_background_jobs_.insert(job); |
304 } | 393 } |
305 } | 394 } |
306 if (job == nullptr) return; | 395 if (job == nullptr) return; |
| 396 |
| 397 if (V8_UNLIKELY(block_for_testing_.Value())) { |
| 398 block_for_testing_.SetValue(false); |
| 399 semaphore_for_testing_.Wait(); |
| 400 } |
| 401 |
307 DoNextStepOnBackgroundThread(job); | 402 DoNextStepOnBackgroundThread(job); |
308 | 403 |
309 ScheduleMoreBackgroundTasksIfNeeded(); | 404 ScheduleMoreBackgroundTasksIfNeeded(); |
310 // Unconditionally schedule an idle task, as all background steps have to be | 405 // Unconditionally schedule an idle task, as all background steps have to be |
311 // followed by a main thread step. | 406 // followed by a main thread step. |
312 ScheduleIdleTaskFromAnyThread(); | 407 ScheduleIdleTaskFromAnyThread(); |
313 | 408 |
314 { | 409 { |
315 base::LockGuard<base::Mutex> lock(&mutex_); | 410 base::LockGuard<base::Mutex> lock(&mutex_); |
316 running_background_jobs_.erase(job); | 411 running_background_jobs_.erase(job); |
317 | 412 |
| 413 if (running_background_jobs_.empty() && abort_) { |
| 414 // This is the last background job that finished. The abort task |
| 415 // scheduled by AbortAll might already have ran, so schedule another |
| 416 // one to be on the safe side. |
| 417 ScheduleAbortTask(); |
| 418 } |
| 419 |
318 if (main_thread_blocking_on_job_ == job) { | 420 if (main_thread_blocking_on_job_ == job) { |
319 main_thread_blocking_on_job_ = nullptr; | 421 main_thread_blocking_on_job_ = nullptr; |
320 main_thread_blocking_signal_.NotifyOne(); | 422 main_thread_blocking_signal_.NotifyOne(); |
321 } | 423 } |
322 } | 424 } |
323 // Don't touch |this| anymore after this point, as it might have been | 425 // Don't touch |this| anymore after this point, as it might have been |
324 // deleted. | 426 // deleted. |
325 } | 427 } |
326 | 428 |
327 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 429 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
| 430 bool aborted = false; |
328 { | 431 { |
329 base::LockGuard<base::Mutex> lock(&mutex_); | 432 base::LockGuard<base::Mutex> lock(&mutex_); |
330 idle_task_scheduled_ = false; | 433 idle_task_scheduled_ = false; |
| 434 aborted = abort_; |
| 435 } |
| 436 |
| 437 if (aborted) { |
| 438 AbortInactiveJobs(); |
| 439 return; |
331 } | 440 } |
332 | 441 |
333 // Number of jobs that are unlikely to make progress during any idle callback | 442 // Number of jobs that are unlikely to make progress during any idle callback |
334 // due to their estimated duration. | 443 // due to their estimated duration. |
335 size_t too_long_jobs = 0; | 444 size_t too_long_jobs = 0; |
336 | 445 |
337 // Iterate over all available jobs & remaining time. For each job, decide | 446 // Iterate over all available jobs & remaining time. For each job, decide |
338 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 447 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
339 // finished), or 3) make progress on it. | 448 // finished), or 3) make progress on it. |
340 double idle_time_in_seconds = | 449 double idle_time_in_seconds = |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 lock.reset(); | 490 lock.reset(); |
382 DoNextStepOnMainThread(isolate_, job->second.get(), | 491 DoNextStepOnMainThread(isolate_, job->second.get(), |
383 ExceptionHandling::kSwallow); | 492 ExceptionHandling::kSwallow); |
384 } | 493 } |
385 } | 494 } |
386 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 495 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
387 } | 496 } |
388 | 497 |
389 } // namespace internal | 498 } // namespace internal |
390 } // namespace v8 | 499 } // namespace v8 |
OLD | NEW |