| 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 |