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