| 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 | |
| 92 // Theoretically we get 50ms of idle time max, however it's unlikely that | 69 // Theoretically we get 50ms of idle time max, however it's unlikely that |
| 93 // we'll get all of it so try to be a conservative. | 70 // we'll get all of it so try to be a conservative. |
| 94 const double kMaxIdleTimeToExpectInMs = 40; | 71 const double kMaxIdleTimeToExpectInMs = 40; |
| 95 | 72 |
| 96 } // namespace | 73 } // namespace |
| 97 | 74 |
| 98 class CompilerDispatcher::BackgroundTask : public CancelableTask { | |
| 99 public: | |
| 100 BackgroundTask(Isolate* isolate, CancelableTaskManager* task_manager, | |
| 101 CompilerDispatcher* dispatcher); | |
| 102 ~BackgroundTask() override; | |
| 103 | |
| 104 // CancelableTask implementation. | |
| 105 void RunInternal() override; | |
| 106 | |
| 107 private: | |
| 108 CompilerDispatcher* dispatcher_; | |
| 109 | |
| 110 DISALLOW_COPY_AND_ASSIGN(BackgroundTask); | |
| 111 }; | |
| 112 | |
| 113 CompilerDispatcher::BackgroundTask::BackgroundTask( | |
| 114 Isolate* isolate, CancelableTaskManager* task_manager, | |
| 115 CompilerDispatcher* dispatcher) | |
| 116 : CancelableTask(isolate, task_manager), dispatcher_(dispatcher) {} | |
| 117 | |
| 118 CompilerDispatcher::BackgroundTask::~BackgroundTask() {} | |
| 119 | |
| 120 void CompilerDispatcher::BackgroundTask::RunInternal() { | |
| 121 dispatcher_->DoBackgroundWork(); | |
| 122 } | |
| 123 | |
| 124 class CompilerDispatcher::IdleTask : public CancelableIdleTask { | 75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
| 125 public: | 76 public: |
| 126 IdleTask(Isolate* isolate, CancelableTaskManager* task_manager, | 77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); |
| 127 CompilerDispatcher* dispatcher); | |
| 128 ~IdleTask() override; | 78 ~IdleTask() override; |
| 129 | 79 |
| 130 // CancelableIdleTask implementation. | 80 // CancelableIdleTask implementation. |
| 131 void RunInternal(double deadline_in_seconds) override; | 81 void RunInternal(double deadline_in_seconds) override; |
| 132 | 82 |
| 133 private: | 83 private: |
| 134 CompilerDispatcher* dispatcher_; | 84 CompilerDispatcher* dispatcher_; |
| 135 | 85 |
| 136 DISALLOW_COPY_AND_ASSIGN(IdleTask); | 86 DISALLOW_COPY_AND_ASSIGN(IdleTask); |
| 137 }; | 87 }; |
| 138 | 88 |
| 139 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | 89 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, |
| 140 CancelableTaskManager* task_manager, | |
| 141 CompilerDispatcher* dispatcher) | 90 CompilerDispatcher* dispatcher) |
| 142 : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {} | 91 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} |
| 143 | 92 |
| 144 CompilerDispatcher::IdleTask::~IdleTask() {} | 93 CompilerDispatcher::IdleTask::~IdleTask() {} |
| 145 | 94 |
| 146 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | 95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { |
| 147 dispatcher_->DoIdleWork(deadline_in_seconds); | 96 dispatcher_->DoIdleWork(deadline_in_seconds); |
| 148 } | 97 } |
| 149 | 98 |
| 150 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
| 151 size_t max_stack_size) | 100 size_t max_stack_size) |
| 152 : isolate_(isolate), | 101 : isolate_(isolate), |
| 153 platform_(platform), | 102 platform_(platform), |
| 154 max_stack_size_(max_stack_size), | 103 max_stack_size_(max_stack_size), |
| 155 tracer_(new CompilerDispatcherTracer(isolate_)), | 104 tracer_(new CompilerDispatcherTracer(isolate_)), |
| 156 task_manager_(new CancelableTaskManager()), | 105 idle_task_scheduled_(false) {} |
| 157 idle_task_scheduled_(false), | |
| 158 num_scheduled_background_tasks_(0), | |
| 159 main_thread_blocking_on_job_(nullptr) {} | |
| 160 | 106 |
| 161 CompilerDispatcher::~CompilerDispatcher() { | 107 CompilerDispatcher::~CompilerDispatcher() { |
| 162 // To avoid crashing in unit tests due to unfished jobs. | 108 // To avoid crashing in unit tests due to unfished jobs. |
| 163 AbortAll(BlockingBehavior::kBlock); | 109 AbortAll(BlockingBehavior::kBlock); |
| 164 task_manager_->CancelAndWait(); | |
| 165 } | 110 } |
| 166 | 111 |
| 167 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
| 168 if (!IsEnabled()) return false; | 113 if (!IsEnabled()) return false; |
| 169 | 114 |
| 170 // We only handle functions (no eval / top-level code / wasm) that are | 115 // We only handle functions (no eval / top-level code / wasm) that are |
| 171 // attached to a script. | 116 // attached to a script. |
| 172 if (!function->script()->IsScript() || !function->is_function() || | 117 if (!function->script()->IsScript() || !function->is_function() || |
| 173 function->asm_function() || function->native()) { | 118 function->asm_function() || function->native()) { |
| 174 return false; | 119 return false; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 186 | 131 |
| 187 bool CompilerDispatcher::IsEnabled() const { | 132 bool CompilerDispatcher::IsEnabled() const { |
| 188 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
| 189 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); | 134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); |
| 190 } | 135 } |
| 191 | 136 |
| 192 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
| 193 return GetJobFor(function) != jobs_.end(); | 138 return GetJobFor(function) != jobs_.end(); |
| 194 } | 139 } |
| 195 | 140 |
| 196 void CompilerDispatcher::WaitForJobIfRunningOnBackground( | |
| 197 CompilerDispatcherJob* job) { | |
| 198 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 199 if (running_background_jobs_.find(job) == running_background_jobs_.end()) { | |
| 200 pending_background_jobs_.erase(job); | |
| 201 return; | |
| 202 } | |
| 203 DCHECK_NULL(main_thread_blocking_on_job_); | |
| 204 main_thread_blocking_on_job_ = job; | |
| 205 while (main_thread_blocking_on_job_ != nullptr) { | |
| 206 main_thread_blocking_signal_.Wait(&mutex_); | |
| 207 } | |
| 208 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end()); | |
| 209 DCHECK(running_background_jobs_.find(job) == running_background_jobs_.end()); | |
| 210 } | |
| 211 | |
| 212 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
| 213 JobMap::const_iterator job = GetJobFor(function); | 142 JobMap::const_iterator job = GetJobFor(function); |
| 214 CHECK(job != jobs_.end()); | 143 CHECK(job != jobs_.end()); |
| 215 | 144 |
| 216 WaitForJobIfRunningOnBackground(job->second.get()); | 145 // TODO(jochen): Check if there's an in-flight background task working on this |
| 146 // job. |
| 217 while (!IsFinished(job->second.get())) { | 147 while (!IsFinished(job->second.get())) { |
| 218 DoNextStepOnMainThread(isolate_, job->second.get(), | 148 DoNextStepOnMainThread(isolate_, job->second.get(), |
| 219 ExceptionHandling::kThrow); | 149 ExceptionHandling::kThrow); |
| 220 } | 150 } |
| 221 bool result = job->second->status() != CompileJobStatus::kFailed; | 151 bool result = job->second->status() != CompileJobStatus::kFailed; |
| 222 job->second->ResetOnMainThread(); | 152 job->second->ResetOnMainThread(); |
| 223 jobs_.erase(job); | 153 jobs_.erase(job); |
| 224 return result; | 154 return result; |
| 225 } | 155 } |
| 226 | 156 |
| 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 |
| 227 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 169 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
| 228 // TODO(jochen): Implement support for non-blocking abort. | 170 USE(blocking); |
| 229 DCHECK(blocking == BlockingBehavior::kBlock); | 171 // TODO(jochen): Check if there's an in-flight background task working on this |
| 172 // job. |
| 230 for (auto& kv : jobs_) { | 173 for (auto& kv : jobs_) { |
| 231 WaitForJobIfRunningOnBackground(kv.second.get()); | |
| 232 kv.second->ResetOnMainThread(); | 174 kv.second->ResetOnMainThread(); |
| 233 } | 175 } |
| 234 jobs_.clear(); | 176 jobs_.clear(); |
| 235 } | 177 } |
| 236 | 178 |
| 237 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
| 238 Handle<SharedFunctionInfo> shared) const { | 180 Handle<SharedFunctionInfo> shared) const { |
| 239 if (!shared->script()->IsScript()) return jobs_.end(); | 181 if (!shared->script()->IsScript()) return jobs_.end(); |
| 240 std::pair<int, int> key(Script::cast(shared->script())->id(), | 182 std::pair<int, int> key(Script::cast(shared->script())->id(), |
| 241 shared->function_literal_id()); | 183 shared->function_literal_id()); |
| 242 auto range = jobs_.equal_range(key); | 184 auto range = jobs_.equal_range(key); |
| 243 for (auto job = range.first; job != range.second; ++job) { | 185 for (auto job = range.first; job != range.second; ++job) { |
| 244 if (job->second->IsAssociatedWith(shared)) return job; | 186 if (job->second->IsAssociatedWith(shared)) return job; |
| 245 } | 187 } |
| 246 return jobs_.end(); | 188 return jobs_.end(); |
| 247 } | 189 } |
| 248 | 190 |
| 249 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { | 191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { |
| 250 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
| 251 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); | 193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); |
| 252 { | 194 if (idle_task_scheduled_) return; |
| 253 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 254 if (idle_task_scheduled_) return; | |
| 255 idle_task_scheduled_ = true; | |
| 256 } | |
| 257 platform_->CallIdleOnForegroundThread( | |
| 258 v8_isolate, new IdleTask(isolate_, task_manager_.get(), this)); | |
| 259 } | |
| 260 | |
| 261 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
| 262 if (jobs_.empty()) return; | 195 if (jobs_.empty()) return; |
| 263 ScheduleIdleTaskFromAnyThread(); | 196 idle_task_scheduled_ = true; |
| 264 } | 197 platform_->CallIdleOnForegroundThread(v8_isolate, |
| 265 | 198 new IdleTask(isolate_, this)); |
| 266 void CompilerDispatcher::ConsiderJobForBackgroundProcessing( | |
| 267 CompilerDispatcherJob* job) { | |
| 268 if (!CanRunOnAnyThread(job)) return; | |
| 269 { | |
| 270 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 271 pending_background_jobs_.insert(job); | |
| 272 } | |
| 273 ScheduleMoreBackgroundTasksIfNeeded(); | |
| 274 } | |
| 275 | |
| 276 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { | |
| 277 if (FLAG_single_threaded) return; | |
| 278 { | |
| 279 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 280 if (pending_background_jobs_.empty()) return; | |
| 281 if (platform_->NumberOfAvailableBackgroundThreads() <= | |
| 282 num_scheduled_background_tasks_) { | |
| 283 return; | |
| 284 } | |
| 285 ++num_scheduled_background_tasks_; | |
| 286 } | |
| 287 platform_->CallOnBackgroundThread( | |
| 288 new BackgroundTask(isolate_, task_manager_.get(), this), | |
| 289 v8::Platform::kShortRunningTask); | |
| 290 } | |
| 291 | |
| 292 void CompilerDispatcher::DoBackgroundWork() { | |
| 293 CompilerDispatcherJob* job = nullptr; | |
| 294 { | |
| 295 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 296 --num_scheduled_background_tasks_; | |
| 297 if (!pending_background_jobs_.empty()) { | |
| 298 auto it = pending_background_jobs_.begin(); | |
| 299 job = *it; | |
| 300 pending_background_jobs_.erase(it); | |
| 301 running_background_jobs_.insert(job); | |
| 302 } | |
| 303 } | |
| 304 if (job == nullptr) return; | |
| 305 DoNextStepOnBackgroundThread(job); | |
| 306 | |
| 307 ScheduleMoreBackgroundTasksIfNeeded(); | |
| 308 // Unconditionally schedule an idle task, as all background steps have to be | |
| 309 // followed by a main thread step. | |
| 310 ScheduleIdleTaskFromAnyThread(); | |
| 311 | |
| 312 { | |
| 313 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 314 running_background_jobs_.erase(job); | |
| 315 | |
| 316 if (main_thread_blocking_on_job_ == job) { | |
| 317 main_thread_blocking_on_job_ = nullptr; | |
| 318 main_thread_blocking_signal_.NotifyOne(); | |
| 319 } | |
| 320 } | |
| 321 // Don't touch |this| anymore after this point, as it might have been | |
| 322 // deleted. | |
| 323 } | 199 } |
| 324 | 200 |
| 325 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
| 326 { | 202 idle_task_scheduled_ = false; |
| 327 base::LockGuard<base::Mutex> lock(&mutex_); | |
| 328 idle_task_scheduled_ = false; | |
| 329 } | |
| 330 | 203 |
| 331 // Number of jobs that are unlikely to make progress during any idle callback | 204 // Number of jobs that are unlikely to make progress during any idle callback |
| 332 // due to their estimated duration. | 205 // due to their estimated duration. |
| 333 size_t too_long_jobs = 0; | 206 size_t too_long_jobs = 0; |
| 334 | 207 |
| 335 // Iterate over all available jobs & remaining time. For each job, decide | 208 // Iterate over all available jobs & remaining time. For each job, decide |
| 336 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | 209 // whether to 1) skip it (if it would take too long), 2) erase it (if it's |
| 337 // finished), or 3) make progress on it. | 210 // finished), or 3) make progress on it. |
| 338 double idle_time_in_seconds = | 211 double idle_time_in_seconds = |
| 339 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | 212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
| 340 for (auto job = jobs_.begin(); | 213 for (auto job = jobs_.begin(); |
| 341 job != jobs_.end() && idle_time_in_seconds > 0.0; | 214 job != jobs_.end() && idle_time_in_seconds > 0.0; |
| 342 idle_time_in_seconds = | 215 idle_time_in_seconds = |
| 343 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | 216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { |
| 344 // Don't work on jobs that are being worked on by background tasks. | |
| 345 // Similarly, remove jobs we work on from the set of available background | |
| 346 // jobs. | |
| 347 std::unique_ptr<base::LockGuard<base::Mutex>> lock( | |
| 348 new base::LockGuard<base::Mutex>(&mutex_)); | |
| 349 if (running_background_jobs_.find(job->second.get()) != | |
| 350 running_background_jobs_.end()) { | |
| 351 ++job; | |
| 352 continue; | |
| 353 } | |
| 354 auto it = pending_background_jobs_.find(job->second.get()); | |
| 355 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | 217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
| 356 if (idle_time_in_seconds < | 218 if (idle_time_in_seconds < |
| 357 (estimate_in_ms / | 219 (estimate_in_ms / |
| 358 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | 220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { |
| 359 // If there's not enough time left, try to estimate whether we would | 221 // If there's not enough time left, try to estimate whether we would |
| 360 // have managed to finish the job in a large idle task to assess | 222 // have managed to finish the job in a large idle task to assess |
| 361 // whether we should ask for another idle callback. | 223 // whether we should ask for another idle callback. |
| 362 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | 224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; |
| 363 if (it == pending_background_jobs_.end()) { | |
| 364 lock.reset(); | |
| 365 ConsiderJobForBackgroundProcessing(job->second.get()); | |
| 366 } | |
| 367 ++job; | 225 ++job; |
| 368 } else if (IsFinished(job->second.get())) { | 226 } else if (IsFinished(job->second.get())) { |
| 369 DCHECK(it == pending_background_jobs_.end()); | |
| 370 job->second->ResetOnMainThread(); | 227 job->second->ResetOnMainThread(); |
| 371 job = jobs_.erase(job); | 228 job = jobs_.erase(job); |
| 372 continue; | 229 break; |
| 373 } else { | 230 } else { |
| 374 // Do one step, and keep processing the job (as we don't advance the | 231 // Do one step, and keep processing the job (as we don't advance the |
| 375 // iterator). | 232 // iterator). |
| 376 if (it != pending_background_jobs_.end()) { | |
| 377 pending_background_jobs_.erase(it); | |
| 378 } | |
| 379 lock.reset(); | |
| 380 DoNextStepOnMainThread(isolate_, job->second.get(), | 233 DoNextStepOnMainThread(isolate_, job->second.get(), |
| 381 ExceptionHandling::kSwallow); | 234 ExceptionHandling::kSwallow); |
| 382 } | 235 } |
| 383 } | 236 } |
| 384 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
| 385 } | 238 } |
| 386 | 239 |
| 387 } // namespace internal | 240 } // namespace internal |
| 388 } // namespace v8 | 241 } // namespace v8 |
| OLD | NEW |