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 <queue> | |
8 | |
9 #include "include/v8-platform.h" | |
10 #include "include/v8.h" | |
11 #include "src/base/platform/time.h" | |
12 #include "src/cancelable-task.h" | |
7 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" | 13 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" |
8 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" | 14 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" |
9 #include "src/objects-inl.h" | 15 #include "src/objects-inl.h" |
10 | 16 |
11 namespace v8 { | 17 namespace v8 { |
12 namespace internal { | 18 namespace internal { |
13 | 19 |
14 namespace { | 20 namespace { |
15 | 21 |
16 bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { | 22 enum class ExceptionHandling { kSwallow, kKeep }; |
23 | |
24 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job, | |
25 ExceptionHandling exception_handling) { | |
26 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); | |
17 switch (job->status()) { | 27 switch (job->status()) { |
18 case CompileJobStatus::kInitial: | 28 case CompileJobStatus::kInitial: |
19 job->PrepareToParseOnMainThread(); | 29 job->PrepareToParseOnMainThread(); |
20 break; | 30 break; |
21 | 31 |
22 case CompileJobStatus::kReadyToParse: | 32 case CompileJobStatus::kReadyToParse: |
23 job->Parse(); | 33 job->Parse(); |
24 break; | 34 break; |
25 | 35 |
26 case CompileJobStatus::kParsed: | 36 case CompileJobStatus::kParsed: |
(...skipping 10 matching lines...) Expand all Loading... | |
37 | 47 |
38 case CompileJobStatus::kCompiled: | 48 case CompileJobStatus::kCompiled: |
39 job->FinalizeCompilingOnMainThread(); | 49 job->FinalizeCompilingOnMainThread(); |
40 break; | 50 break; |
41 | 51 |
42 case CompileJobStatus::kFailed: | 52 case CompileJobStatus::kFailed: |
43 case CompileJobStatus::kDone: | 53 case CompileJobStatus::kDone: |
44 break; | 54 break; |
45 } | 55 } |
46 | 56 |
57 if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) { | |
58 try_catch.ReThrow(); | |
59 } | |
marja
2016/12/13 09:09:01
Can we assert here that if the try catch has caugh
jochen (gone - plz use gerrit)
2016/12/15 13:54:34
done
| |
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 |
55 } // namespace | 68 } // namespace |
56 | 69 |
57 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size) | 70 class CompilerDispatcher::IdleTask : public CancelableIdleTask { |
71 public: | |
72 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); | |
73 ~IdleTask() override; | |
74 | |
75 // CancelableIdleTask implementation. | |
vogelheim
2016/12/12 17:51:32
I don't understand the comment. If the intention i
jochen (gone - plz use gerrit)
2016/12/12 20:28:25
it's a common pattern in chromium code as it has m
vogelheim
2016/12/13 09:40:33
Acknowledged.
| |
76 void RunInternal(double deadline_in_seconds) override; | |
77 | |
78 private: | |
79 CompilerDispatcher* dispatcher_; | |
80 | |
81 DISALLOW_COPY_AND_ASSIGN(IdleTask); | |
82 }; | |
83 | |
84 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate, | |
85 CompilerDispatcher* dispatcher) | |
86 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {} | |
87 | |
88 CompilerDispatcher::IdleTask::~IdleTask() {} | |
89 | |
90 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { | |
91 dispatcher_->DoIdleWork(deadline_in_seconds); | |
92 } | |
93 | |
94 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, | |
95 size_t max_stack_size) | |
58 : isolate_(isolate), | 96 : isolate_(isolate), |
97 platform_(platform), | |
59 max_stack_size_(max_stack_size), | 98 max_stack_size_(max_stack_size), |
60 tracer_(new CompilerDispatcherTracer(isolate_)) {} | 99 tracer_(new CompilerDispatcherTracer(isolate_)), |
100 idle_task_scheduled_(false) {} | |
61 | 101 |
62 CompilerDispatcher::~CompilerDispatcher() {} | 102 CompilerDispatcher::~CompilerDispatcher() {} |
63 | 103 |
64 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { | 104 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { |
65 // We only handle functions (no eval / top-level code / wasm) that are | 105 // We only handle functions (no eval / top-level code / wasm) that are |
66 // attached to a script. | 106 // attached to a script. |
67 if (!function->script()->IsScript() || !function->is_function() || | 107 if (!function->script()->IsScript() || !function->is_function() || |
68 function->asm_function() || function->native()) { | 108 function->asm_function() || function->native()) { |
69 return false; | 109 return false; |
70 } | 110 } |
71 | 111 |
72 if (IsEnqueued(function)) return true; | 112 if (IsEnqueued(function)) return true; |
73 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( | 113 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( |
74 isolate_, tracer_.get(), function, max_stack_size_)); | 114 isolate_, tracer_.get(), function, max_stack_size_)); |
75 std::pair<int, int> key(Script::cast(function->script())->id(), | 115 std::pair<int, int> key(Script::cast(function->script())->id(), |
76 function->function_literal_id()); | 116 function->function_literal_id()); |
77 jobs_.insert(std::make_pair(key, std::move(job))); | 117 jobs_.insert(std::make_pair(key, std::move(job))); |
118 ScheduleIdleTaskIfNeeded(); | |
78 return true; | 119 return true; |
79 } | 120 } |
80 | 121 |
81 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { | 122 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { |
82 return GetJobFor(function) != jobs_.end(); | 123 return GetJobFor(function) != jobs_.end(); |
83 } | 124 } |
84 | 125 |
85 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { | 126 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { |
86 JobMap::const_iterator job = GetJobFor(function); | 127 JobMap::const_iterator job = GetJobFor(function); |
87 CHECK(job != jobs_.end()); | 128 CHECK(job != jobs_.end()); |
88 | 129 |
89 // TODO(jochen): Check if there's an in-flight background task working on this | 130 // TODO(jochen): Check if there's an in-flight background task working on this |
90 // job. | 131 // job. |
91 while (!IsFinished(job->second.get())) { | 132 while (!IsFinished(job->second.get())) { |
92 DoNextStepOnMainThread(job->second.get()); | 133 DoNextStepOnMainThread(isolate_, job->second.get(), |
134 ExceptionHandling::kKeep); | |
93 } | 135 } |
94 bool result = job->second->status() != CompileJobStatus::kFailed; | 136 bool result = job->second->status() != CompileJobStatus::kFailed; |
137 job->second->ResetOnMainThread(); | |
95 jobs_.erase(job); | 138 jobs_.erase(job); |
96 return result; | 139 return result; |
97 } | 140 } |
98 | 141 |
99 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, | 142 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, |
100 BlockingBehavior blocking) { | 143 BlockingBehavior blocking) { |
101 USE(blocking); | 144 USE(blocking); |
102 JobMap::const_iterator job = GetJobFor(function); | 145 JobMap::const_iterator job = GetJobFor(function); |
103 CHECK(job != jobs_.end()); | 146 CHECK(job != jobs_.end()); |
104 | 147 |
105 // TODO(jochen): Check if there's an in-flight background task working on this | 148 // TODO(jochen): Check if there's an in-flight background task working on this |
106 // job. | 149 // job. |
150 job->second->ResetOnMainThread(); | |
107 jobs_.erase(job); | 151 jobs_.erase(job); |
108 } | 152 } |
109 | 153 |
110 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { | 154 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { |
111 USE(blocking); | 155 USE(blocking); |
112 // 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 |
113 // job. | 157 // job. |
158 for (auto& kv : jobs_) { | |
159 kv.second->ResetOnMainThread(); | |
160 } | |
114 jobs_.clear(); | 161 jobs_.clear(); |
115 } | 162 } |
116 | 163 |
117 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( | 164 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( |
118 Handle<SharedFunctionInfo> shared) const { | 165 Handle<SharedFunctionInfo> shared) const { |
119 if (!shared->script()->IsScript()) return jobs_.end(); | 166 if (!shared->script()->IsScript()) return jobs_.end(); |
120 std::pair<int, int> key(Script::cast(shared->script())->id(), | 167 std::pair<int, int> key(Script::cast(shared->script())->id(), |
121 shared->function_literal_id()); | 168 shared->function_literal_id()); |
122 auto range = jobs_.equal_range(key); | 169 auto range = jobs_.equal_range(key); |
123 for (auto job = range.first; job != range.second; ++job) { | 170 for (auto job = range.first; job != range.second; ++job) { |
124 if (job->second->IsAssociatedWith(shared)) return job; | 171 if (job->second->IsAssociatedWith(shared)) return job; |
125 } | 172 } |
126 return jobs_.end(); | 173 return jobs_.end(); |
127 } | 174 } |
128 | 175 |
176 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { | |
177 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |
178 if (!platform_->IdleTasksEnabled(v8_isolate)) return; | |
179 if (idle_task_scheduled_) return; | |
180 if (jobs_.empty()) return; | |
181 idle_task_scheduled_ = true; | |
182 platform_->CallIdleOnForegroundThread(v8_isolate, | |
183 new IdleTask(isolate_, this)); | |
184 } | |
185 | |
186 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { | |
187 idle_task_scheduled_ = false; | |
marja
2016/12/13 09:09:01
Could you add assert(we're on the main thread) to
| |
188 | |
189 // Collect all candidates for running their respective next step during | |
190 // idle time and sort by how long they're likely to take. | |
marja
2016/12/13 09:09:01
Hmm, it looks inefficient to iterate through all i
| |
191 auto cmp = [](const std::pair<double, JobMap::const_iterator>& a, | |
192 const std::pair<double, JobMap::const_iterator>& b) { | |
193 return a.first < b.first; | |
194 }; | |
195 std::priority_queue<std::pair<double, JobMap::const_iterator>, | |
196 std::vector<std::pair<double, JobMap::const_iterator>>, | |
197 decltype(cmp)> | |
198 runtimes(cmp); | |
199 for (JobMap::const_iterator it = jobs_.begin(); it != jobs_.end(); ++it) { | |
200 if (IsFinished(it->second.get())) continue; | |
201 // TODO(jochen): Check if there's an in-flight background task working on | |
202 // this job. | |
203 runtimes.push(std::make_pair( | |
204 it->second->EstimateRuntimeOfNextStepInMs() / | |
vogelheim
2016/12/12 17:51:32
Every one of these will look the same lock, right?
jochen (gone - plz use gerrit)
2016/12/12 20:28:25
right, dunno how much of an issue that is?
| |
205 static_cast<double>(base::Time::kMillisecondsPerSecond), | |
206 it)); | |
207 } | |
208 | |
209 for (double idle_time_in_seconds = | |
marja
2016/12/13 09:21:13
What if coming up with the priority queue takes so
| |
210 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); | |
211 idle_time_in_seconds > 0.0; | |
212 idle_time_in_seconds = | |
213 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { | |
marja
2016/12/13 09:09:01
This loop is mildly confusing.
Why not just:
whi
| |
214 // Skip jobs we can't do in the remaining time budget. | |
215 while (!runtimes.empty() && runtimes.top().first > idle_time_in_seconds) { | |
216 runtimes.pop(); | |
vogelheim
2016/12/12 17:51:32
If runtimes is a priority queue by expected runtim
vogelheim
2016/12/12 17:51:32
[Not sure:] What happens when there is a job whose
jochen (gone - plz use gerrit)
2016/12/12 20:28:25
So far, the biggest task comes first. I guess I co
vogelheim
2016/12/13 09:40:33
Ah, I misread that and assumed we'd process tasks
| |
217 } | |
218 if (runtimes.empty()) break; | |
219 | |
220 JobMap::const_iterator job = runtimes.top().second; | |
221 runtimes.pop(); | |
222 DoNextStepOnMainThread(isolate_, job->second.get(), | |
223 ExceptionHandling::kSwallow); | |
224 | |
225 if (IsFinished(job->second.get())) { | |
226 job->second->ResetOnMainThread(); | |
227 jobs_.erase(job); | |
228 continue; | |
229 } | |
230 | |
231 // Put the job back on the queue in case there is time left. | |
232 runtimes.push(std::make_pair( | |
233 job->second->EstimateRuntimeOfNextStepInMs() / | |
234 static_cast<double>(base::Time::kMillisecondsPerSecond), | |
235 job)); | |
vogelheim
2016/12/12 17:51:32
[Not sure:] It might make sense to store
auto cu
jochen (gone - plz use gerrit)
2016/12/12 20:28:25
we still need the mutex, because after having made
vogelheim
2016/12/13 09:40:33
Ah.
For my understanding: Why would the time esti
jochen (gone - plz use gerrit)
2016/12/15 13:54:34
in line 222 the job does one step, and i gets rein
| |
236 } | |
237 ScheduleIdleTaskIfNeeded(); | |
238 } | |
239 | |
129 } // namespace internal | 240 } // namespace internal |
130 } // namespace v8 | 241 } // namespace v8 |
OLD | NEW |