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, 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 |
75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { | 124 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
76 public: | 125 public: |
77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); | 126 IdleTask(Isolate* isolate, CancelableTaskManager* task_manager, |
| 127 CompilerDispatcher* dispatcher); |
78 ~IdleTask() override; | 128 ~IdleTask() override; |
79 | 129 |
80 // CancelableIdleTask implementation. | 130 // CancelableIdleTask implementation. |
81 void RunInternal(double deadline_in_seconds) override; | 131 void RunInternal(double deadline_in_seconds) override; |
82 | 132 |
83 private: | 133 private: |
84 CompilerDispatcher* dispatcher_; | 134 CompilerDispatcher* dispatcher_; |
85 | 135 |
86 DISALLOW_COPY_AND_ASSIGN(IdleTask); | 136 DISALLOW_COPY_AND_ASSIGN(IdleTask); |
87 }; | 137 }; |
88 | 138 |
89 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | 139 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, |
| 140 CancelableTaskManager* task_manager, |
90 CompilerDispatcher* dispatcher) | 141 CompilerDispatcher* dispatcher) |
91 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} | 142 : CancelableIdleTask(isolate, task_manager), dispatcher_(dispatcher) {} |
92 | 143 |
93 CompilerDispatcher::IdleTask::~IdleTask() {} | 144 CompilerDispatcher::IdleTask::~IdleTask() {} |
94 | 145 |
95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | 146 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { |
96 dispatcher_->DoIdleWork(deadline_in_seconds); | 147 dispatcher_->DoIdleWork(deadline_in_seconds); |
97 } | 148 } |
98 | 149 |
99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | 150 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, |
100 size_t max_stack_size) | 151 size_t max_stack_size) |
101 : isolate_(isolate), | 152 : isolate_(isolate), |
102 platform_(platform), | 153 platform_(platform), |
103 max_stack_size_(max_stack_size), | 154 max_stack_size_(max_stack_size), |
104 tracer_(new CompilerDispatcherTracer(isolate_)), | 155 tracer_(new CompilerDispatcherTracer(isolate_)), |
105 idle_task_scheduled_(false) {} | 156 task_manager_(new CancelableTaskManager()), |
| 157 idle_task_scheduled_(false), |
| 158 num_scheduled_background_tasks_(0), |
| 159 main_thread_blocking_on_job_(nullptr) {} |
106 | 160 |
107 CompilerDispatcher::~CompilerDispatcher() { | 161 CompilerDispatcher::~CompilerDispatcher() { |
108 // To avoid crashing in unit tests due to unfished jobs. | 162 // To avoid crashing in unit tests due to unfished jobs. |
109 AbortAll(BlockingBehavior::kBlock); | 163 AbortAll(BlockingBehavior::kBlock); |
| 164 task_manager_->CancelAndWait(); |
110 } | 165 } |
111 | 166 |
112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 167 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
113 if (!IsEnabled()) return false; | 168 if (!IsEnabled()) return false; |
114 | 169 |
115 // We only handle functions (no eval / top-level code / wasm) that are | 170 // We only handle functions (no eval / top-level code / wasm) that are |
116 // attached to a script. | 171 // attached to a script. |
117 if (!function->script()->IsScript() || !function->is_function() || | 172 if (!function->script()->IsScript() || !function->is_function() || |
118 function->asm_function() || function->native()) { | 173 function->asm_function() || function->native()) { |
119 return false; | 174 return false; |
(...skipping 11 matching lines...) Expand all Loading... |
131 | 186 |
132 bool CompilerDispatcher::IsEnabled() const { | 187 bool CompilerDispatcher::IsEnabled() const { |
133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 188 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); | 189 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); |
135 } | 190 } |
136 | 191 |
137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 192 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
138 return GetJobFor(function) != jobs_.end(); | 193 return GetJobFor(function) != jobs_.end(); |
139 } | 194 } |
140 | 195 |
| 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 |
141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 212 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
142 JobMap::const_iterator job = GetJobFor(function); | 213 JobMap::const_iterator job = GetJobFor(function); |
143 CHECK(job != jobs_.end()); | 214 CHECK(job != jobs_.end()); |
144 | 215 |
145 // TODO(jochen): Check if there's an in-flight background task working on this | 216 WaitForJobIfRunningOnBackground(job->second.get()); |
146 // job. | |
147 while (!IsFinished(job->second.get())) { | 217 while (!IsFinished(job->second.get())) { |
148 DoNextStepOnMainThread(isolate_, job->second.get(), | 218 DoNextStepOnMainThread(isolate_, job->second.get(), |
149 ExceptionHandling::kThrow); | 219 ExceptionHandling::kThrow); |
150 } | 220 } |
151 bool result = job->second->status() != CompileJobStatus::kFailed; | 221 bool result = job->second->status() != CompileJobStatus::kFailed; |
152 job->second->ResetOnMainThread(); | 222 job->second->ResetOnMainThread(); |
153 jobs_.erase(job); | 223 jobs_.erase(job); |
154 return result; | 224 return result; |
155 } | 225 } |
156 | 226 |
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) { | 227 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
170 USE(blocking); | 228 // TODO(jochen): Implement support for non-blocking abort. |
171 // TODO(jochen): Check if there's an in-flight background task working on this | 229 DCHECK(blocking == BlockingBehavior::kBlock); |
172 // job. | |
173 for (auto& kv : jobs_) { | 230 for (auto& kv : jobs_) { |
| 231 WaitForJobIfRunningOnBackground(kv.second.get()); |
174 kv.second->ResetOnMainThread(); | 232 kv.second->ResetOnMainThread(); |
175 } | 233 } |
176 jobs_.clear(); | 234 jobs_.clear(); |
177 } | 235 } |
178 | 236 |
179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 237 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
180 Handle<SharedFunctionInfo> shared) const { | 238 Handle<SharedFunctionInfo> shared) const { |
181 if (!shared->script()->IsScript()) return jobs_.end(); | 239 if (!shared->script()->IsScript()) return jobs_.end(); |
182 std::pair<int, int> key(Script::cast(shared->script())->id(), | 240 std::pair<int, int> key(Script::cast(shared->script())->id(), |
183 shared->function_literal_id()); | 241 shared->function_literal_id()); |
184 auto range = jobs_.equal_range(key); | 242 auto range = jobs_.equal_range(key); |
185 for (auto job = range.first; job != range.second; ++job) { | 243 for (auto job = range.first; job != range.second; ++job) { |
186 if (job->second->IsAssociatedWith(shared)) return job; | 244 if (job->second->IsAssociatedWith(shared)) return job; |
187 } | 245 } |
188 return jobs_.end(); | 246 return jobs_.end(); |
189 } | 247 } |
190 | 248 |
191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | 249 void CompilerDispatcher::ScheduleIdleTaskFromAnyThread() { |
192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | 250 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); |
193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); | 251 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); |
194 if (idle_task_scheduled_) return; | 252 { |
| 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() { |
195 if (jobs_.empty()) return; | 262 if (jobs_.empty()) return; |
196 idle_task_scheduled_ = true; | 263 ScheduleIdleTaskFromAnyThread(); |
197 platform_->CallIdleOnForegroundThread(v8_isolate, | 264 } |
198 new IdleTask(isolate_, this)); | 265 |
| 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. |
199 } | 323 } |
200 | 324 |
201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | 325 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { |
202 idle_task_scheduled_ = false; | 326 { |
| 327 base::LockGuard<base::Mutex> lock(&mutex_); |
| 328 idle_task_scheduled_ = false; |
| 329 } |
203 | 330 |
204 // Number of jobs that are unlikely to make progress during any idle callback | 331 // Number of jobs that are unlikely to make progress during any idle callback |
205 // due to their estimated duration. | 332 // due to their estimated duration. |
206 size_t too_long_jobs = 0; | 333 size_t too_long_jobs = 0; |
207 | 334 |
208 // Iterate over all available jobs & remaining time. For each job, decide | 335 // 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 | 336 // 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. | 337 // finished), or 3) make progress on it. |
211 double idle_time_in_seconds = | 338 double idle_time_in_seconds = |
212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | 339 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); |
213 for (auto job = jobs_.begin(); | 340 for (auto job = jobs_.begin(); |
214 job != jobs_.end() && idle_time_in_seconds > 0.0; | 341 job != jobs_.end() && idle_time_in_seconds > 0.0; |
215 idle_time_in_seconds = | 342 idle_time_in_seconds = |
216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | 343 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()); |
217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | 355 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); |
218 if (idle_time_in_seconds < | 356 if (idle_time_in_seconds < |
219 (estimate_in_ms / | 357 (estimate_in_ms / |
220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | 358 static_cast<double>(base::Time::kMillisecondsPerSecond))) { |
221 // If there's not enough time left, try to estimate whether we would | 359 // 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 | 360 // have managed to finish the job in a large idle task to assess |
223 // whether we should ask for another idle callback. | 361 // whether we should ask for another idle callback. |
224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | 362 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; |
| 363 if (it == pending_background_jobs_.end()) { |
| 364 lock.reset(); |
| 365 ConsiderJobForBackgroundProcessing(job->second.get()); |
| 366 } |
225 ++job; | 367 ++job; |
226 } else if (IsFinished(job->second.get())) { | 368 } else if (IsFinished(job->second.get())) { |
| 369 DCHECK(it == pending_background_jobs_.end()); |
227 job->second->ResetOnMainThread(); | 370 job->second->ResetOnMainThread(); |
228 job = jobs_.erase(job); | 371 job = jobs_.erase(job); |
229 break; | 372 continue; |
230 } else { | 373 } else { |
231 // Do one step, and keep processing the job (as we don't advance the | 374 // Do one step, and keep processing the job (as we don't advance the |
232 // iterator). | 375 // iterator). |
| 376 if (it != pending_background_jobs_.end()) { |
| 377 pending_background_jobs_.erase(it); |
| 378 } |
| 379 lock.reset(); |
233 DoNextStepOnMainThread(isolate_, job->second.get(), | 380 DoNextStepOnMainThread(isolate_, job->second.get(), |
234 ExceptionHandling::kSwallow); | 381 ExceptionHandling::kSwallow); |
235 } | 382 } |
236 } | 383 } |
237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | 384 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); |
238 } | 385 } |
239 | 386 |
240 } // namespace internal | 387 } // namespace internal |
241 } // namespace v8 | 388 } // namespace v8 |
OLD | NEW |