Chromium Code Reviews| 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" | |
| 8 #include "include/v8.h" | |
| 9 #include "src/base/platform/time.h" | |
| 10 #include "src/cancelable-task.h" | |
| 7 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" | 11 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" |
| 8 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" | 12 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" |
| 9 #include "src/objects-inl.h" | 13 #include "src/objects-inl.h" |
| 10 | 14 |
| 11 namespace v8 { | 15 namespace v8 { |
| 12 namespace internal { | 16 namespace internal { |
| 13 | 17 |
| 14 namespace { | 18 namespace { |
| 15 | 19 |
| 16 bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { | 20 enum class ExceptionHandling { kSwallow, kKeep }; |
| 21 | |
| 22 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job, | |
| 23 ExceptionHandling exception_handling) { | |
| 24 DCHECK(ThreadId::Current().Equals(isolate->thread_id())); | |
| 25 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); | |
| 17 switch (job->status()) { | 26 switch (job->status()) { |
| 18 case CompileJobStatus::kInitial: | 27 case CompileJobStatus::kInitial: |
| 19 job->PrepareToParseOnMainThread(); | 28 job->PrepareToParseOnMainThread(); |
| 20 break; | 29 break; |
| 21 | 30 |
| 22 case CompileJobStatus::kReadyToParse: | 31 case CompileJobStatus::kReadyToParse: |
| 23 job->Parse(); | 32 job->Parse(); |
| 24 break; | 33 break; |
| 25 | 34 |
| 26 case CompileJobStatus::kParsed: | 35 case CompileJobStatus::kParsed: |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 37 | 46 |
| 38 case CompileJobStatus::kCompiled: | 47 case CompileJobStatus::kCompiled: |
| 39 job->FinalizeCompilingOnMainThread(); | 48 job->FinalizeCompilingOnMainThread(); |
| 40 break; | 49 break; |
| 41 | 50 |
| 42 case CompileJobStatus::kFailed: | 51 case CompileJobStatus::kFailed: |
| 43 case CompileJobStatus::kDone: | 52 case CompileJobStatus::kDone: |
| 44 break; | 53 break; |
| 45 } | 54 } |
| 46 | 55 |
| 56 if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) { | |
| 57 DCHECK(job->status() == CompileJobStatus::kFailed); | |
| 58 try_catch.ReThrow(); | |
| 59 } | |
| 47 return job->status() != CompileJobStatus::kFailed; | 60 return job->status() != CompileJobStatus::kFailed; |
| 48 } | 61 } |
| 49 | 62 |
| 50 bool IsFinished(CompilerDispatcherJob* job) { | 63 bool IsFinished(CompilerDispatcherJob* job) { |
| 51 return job->status() == CompileJobStatus::kDone || | 64 return job->status() == CompileJobStatus::kDone || |
| 52 job->status() == CompileJobStatus::kFailed; | 65 job->status() == CompileJobStatus::kFailed; |
| 53 } | 66 } |
| 54 | 67 |
| 68 // Theoretically we get 50ms of idle time max, however it's unlikely that | |
| 69 // we'll get all of it so try to be a conservative. | |
| 70 const double kMaxIdleTimeToExpectInMs = 40; | |
| 71 | |
| 55 } // namespace | 72 } // namespace |
| 56 | 73 |
| 57 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size) | 74 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
| 75 public: | |
| 76 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); | |
| 77 ~IdleTask() override; | |
| 78 | |
| 79 // CancelableIdleTask implementation. | |
| 80 void RunInternal(double deadline_in_seconds) override; | |
| 81 | |
| 82 private: | |
| 83 CompilerDispatcher* dispatcher_; | |
| 84 | |
| 85 DISALLOW_COPY_AND_ASSIGN(IdleTask); | |
| 86 }; | |
| 87 | |
| 88 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | |
| 89 CompilerDispatcher* dispatcher) | |
| 90 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} | |
| 91 | |
| 92 CompilerDispatcher::IdleTask::~IdleTask() {} | |
| 93 | |
| 94 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | |
| 95 dispatcher_->DoIdleWork(deadline_in_seconds); | |
| 96 } | |
| 97 | |
| 98 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | |
| 99 size_t max_stack_size) | |
| 58 : isolate_(isolate), | 100 : isolate_(isolate), |
| 101 platform_(platform), | |
| 59 max_stack_size_(max_stack_size), | 102 max_stack_size_(max_stack_size), |
| 60 tracer_(new CompilerDispatcherTracer(isolate_)) {} | 103 tracer_(new CompilerDispatcherTracer(isolate_)), |
| 104 idle_task_scheduled_(false) {} | |
| 61 | 105 |
| 62 CompilerDispatcher::~CompilerDispatcher() {} | 106 CompilerDispatcher::~CompilerDispatcher() { |
| 107 // To avoid crashing in unit tests due to unfished jobs. | |
| 108 AbortAll(BlockingBehavior::kBlock); | |
| 109 } | |
| 63 | 110 |
| 64 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 111 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
| 65 // We only handle functions (no eval / top-level code / wasm) that are | 112 // We only handle functions (no eval / top-level code / wasm) that are |
| 66 // attached to a script. | 113 // attached to a script. |
| 67 if (!function->script()->IsScript() || !function->is_function() || | 114 if (!function->script()->IsScript() || !function->is_function() || |
| 68 function->asm_function() || function->native()) { | 115 function->asm_function() || function->native()) { |
| 69 return false; | 116 return false; |
| 70 } | 117 } |
| 71 | 118 |
| 72 if (IsEnqueued(function)) return true; | 119 if (IsEnqueued(function)) return true; |
| 73 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 120 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
| 74 isolate_, tracer_.get(), function, max_stack_size_)); | 121 isolate_, tracer_.get(), function, max_stack_size_)); |
| 75 std::pair<int, int> key(Script::cast(function->script())->id(), | 122 std::pair<int, int> key(Script::cast(function->script())->id(), |
| 76 function->function_literal_id()); | 123 function->function_literal_id()); |
| 77 jobs_.insert(std::make_pair(key, std::move(job))); | 124 jobs_.insert(std::make_pair(key, std::move(job))); |
| 125 ScheduleIdleTaskIfNeeded(); | |
| 78 return true; | 126 return true; |
| 79 } | 127 } |
| 80 | 128 |
| 81 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 129 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
| 82 return GetJobFor(function) != jobs_.end(); | 130 return GetJobFor(function) != jobs_.end(); |
| 83 } | 131 } |
| 84 | 132 |
| 85 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 133 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
| 86 JobMap::const_iterator job = GetJobFor(function); | 134 JobMap::const_iterator job = GetJobFor(function); |
| 87 CHECK(job != jobs_.end()); | 135 CHECK(job != jobs_.end()); |
| 88 | 136 |
| 89 // TODO(jochen): Check if there's an in-flight background task working on this | 137 // TODO(jochen): Check if there's an in-flight background task working on this |
| 90 // job. | 138 // job. |
| 91 while (!IsFinished(job->second.get())) { | 139 while (!IsFinished(job->second.get())) { |
| 92 DoNextStepOnMainThread(job->second.get()); | 140 DoNextStepOnMainThread(isolate_, job->second.get(), |
| 141 ExceptionHandling::kKeep); | |
| 93 } | 142 } |
| 94 bool result = job->second->status() != CompileJobStatus::kFailed; | 143 bool result = job->second->status() != CompileJobStatus::kFailed; |
| 144 job->second->ResetOnMainThread(); | |
| 95 jobs_.erase(job); | 145 jobs_.erase(job); |
| 96 return result; | 146 return result; |
| 97 } | 147 } |
| 98 | 148 |
| 99 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, | 149 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
| 100 BlockingBehavior blocking) { | 150 BlockingBehavior blocking) { |
| 101 USE(blocking); | 151 USE(blocking); |
| 102 JobMap::const_iterator job = GetJobFor(function); | 152 JobMap::const_iterator job = GetJobFor(function); |
| 103 CHECK(job != jobs_.end()); | 153 CHECK(job != jobs_.end()); |
| 104 | 154 |
| 105 // TODO(jochen): Check if there's an in-flight background task working on this | 155 // TODO(jochen): Check if there's an in-flight background task working on this |
| 106 // job. | 156 // job. |
| 157 job->second->ResetOnMainThread(); | |
| 107 jobs_.erase(job); | 158 jobs_.erase(job); |
| 108 } | 159 } |
| 109 | 160 |
| 110 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 161 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
| 111 USE(blocking); | 162 USE(blocking); |
| 112 // TODO(jochen): Check if there's an in-flight background task working on this | 163 // TODO(jochen): Check if there's an in-flight background task working on this |
| 113 // job. | 164 // job. |
| 165 for (auto& kv : jobs_) { | |
| 166 kv.second->ResetOnMainThread(); | |
| 167 } | |
| 114 jobs_.clear(); | 168 jobs_.clear(); |
| 115 } | 169 } |
| 116 | 170 |
| 117 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 171 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
| 118 Handle<SharedFunctionInfo> shared) const { | 172 Handle<SharedFunctionInfo> shared) const { |
| 119 if (!shared->script()->IsScript()) return jobs_.end(); | 173 if (!shared->script()->IsScript()) return jobs_.end(); |
| 120 std::pair<int, int> key(Script::cast(shared->script())->id(), | 174 std::pair<int, int> key(Script::cast(shared->script())->id(), |
| 121 shared->function_literal_id()); | 175 shared->function_literal_id()); |
| 122 auto range = jobs_.equal_range(key); | 176 auto range = jobs_.equal_range(key); |
| 123 for (auto job = range.first; job != range.second; ++job) { | 177 for (auto job = range.first; job != range.second; ++job) { |
| 124 if (job->second->IsAssociatedWith(shared)) return job; | 178 if (job->second->IsAssociatedWith(shared)) return job; |
| 125 } | 179 } |
| 126 return jobs_.end(); | 180 return jobs_.end(); |
| 127 } | 181 } |
| 128 | 182 |
| 183 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
| 184 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |
| 185 if (!platform_->IdleTasksEnabled(v8_isolate)) return; | |
| 186 if (idle_task_scheduled_) return; | |
| 187 if (jobs_.empty()) return; | |
| 188 idle_task_scheduled_ = true; | |
| 189 platform_->CallIdleOnForegroundThread(v8_isolate, | |
| 190 new IdleTask(isolate_, this)); | |
| 191 } | |
| 192 | |
| 193 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | |
| 194 idle_task_scheduled_ = false; | |
| 195 | |
| 196 // Number of jobs that are unlikely to make progress during any idle callback | |
| 197 // due to their estimated duration. | |
| 198 size_t too_long_jobs = 0; | |
| 199 | |
| 200 // Iterate over all available jobs & remaining time. For each job, decide | |
| 201 // whether to 1) skip it (if it would take too long), 2) erase it (if it's | |
| 202 // finished), or 3) make progress on it. | |
| 203 double idle_time_in_seconds = | |
| 204 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | |
| 205 for (auto job = jobs_.begin(); | |
| 206 job != jobs_.end() && idle_time_in_seconds > 0.0; | |
| 207 idle_time_in_seconds = | |
| 208 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | |
| 209 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); | |
| 210 if (idle_time_in_seconds < | |
| 211 (estimate_in_ms / | |
| 212 static_cast<double>(base::Time::kMillisecondsPerSecond))) { | |
| 213 // If there's not enough time left, try to estimate whether we would | |
| 214 // have managed to finish the job in a large idle task to assess | |
| 215 // whether we should ask for another idle callback. | |
| 216 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; | |
| 217 ++job; | |
| 218 } else if (IsFinished(job->second.get())) { | |
| 219 job->second->ResetOnMainThread(); | |
| 220 job = jobs_.erase(job); | |
| 221 break; | |
| 222 } else { | |
| 223 DoNextStepOnMainThread(isolate_, job->second.get(), | |
|
marja
2016/12/16 12:00:24
One (possibly unintended?) property of this code i
| |
| 224 ExceptionHandling::kSwallow); | |
| 225 } | |
| 226 } | |
| 227 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); | |
| 228 } | |
| 229 | |
| 129 } // namespace internal | 230 } // namespace internal |
| 130 } // namespace v8 | 231 } // namespace v8 |
| OLD | NEW |