1 |
|
|
#include "node_platform.h" |
2 |
|
|
#include "node_internals.h" |
3 |
|
|
|
4 |
|
|
#include "env-inl.h" |
5 |
|
|
#include "debug_utils-inl.h" |
6 |
|
|
#include <algorithm> // find_if(), find(), move() |
7 |
|
|
#include <cmath> // llround() |
8 |
|
|
#include <memory> // unique_ptr(), shared_ptr(), make_shared() |
9 |
|
|
|
10 |
|
|
namespace node { |
11 |
|
|
|
12 |
|
|
using v8::Isolate; |
13 |
|
|
using v8::Object; |
14 |
|
|
using v8::Platform; |
15 |
|
|
using v8::Task; |
16 |
|
|
|
17 |
|
|
namespace { |
18 |
|
|
|
19 |
|
|
struct PlatformWorkerData { |
20 |
|
|
TaskQueue<Task>* task_queue; |
21 |
|
|
Mutex* platform_workers_mutex; |
22 |
|
|
ConditionVariable* platform_workers_ready; |
23 |
|
|
int* pending_platform_workers; |
24 |
|
|
int id; |
25 |
|
|
}; |
26 |
|
|
|
27 |
|
18418 |
static void PlatformWorkerThread(void* data) { |
28 |
|
|
std::unique_ptr<PlatformWorkerData> |
29 |
|
36820 |
worker_data(static_cast<PlatformWorkerData*>(data)); |
30 |
|
|
|
31 |
|
18423 |
TaskQueue<Task>* pending_worker_tasks = worker_data->task_queue; |
32 |
|
|
TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", |
33 |
✓✓ |
18421 |
"PlatformWorkerThread"); |
34 |
|
|
|
35 |
|
4622 |
// Notify the main thread that the platform worker is ready. |
36 |
✓✓ |
18423 |
{ |
37 |
|
36853 |
Mutex::ScopedLock lock(*worker_data->platform_workers_mutex); |
38 |
|
18430 |
(*worker_data->pending_platform_workers)--; |
39 |
|
18430 |
worker_data->platform_workers_ready->Signal(lock); |
40 |
|
|
} |
41 |
|
|
|
42 |
✓✓ |
119303 |
while (std::unique_ptr<Task> task = pending_worker_tasks->BlockingPop()) { |
43 |
|
50410 |
task->Run(); |
44 |
✓✓ |
50384 |
pending_worker_tasks->NotifyOfCompletion(); |
45 |
|
50439 |
} |
46 |
|
18401 |
} |
47 |
|
|
|
48 |
|
|
} // namespace |
49 |
|
|
|
50 |
|
4599 |
class WorkerThreadsTaskRunner::DelayedTaskScheduler { |
51 |
|
|
public: |
52 |
|
4606 |
explicit DelayedTaskScheduler(TaskQueue<Task>* tasks) |
53 |
|
4606 |
: pending_worker_tasks_(tasks) {} |
54 |
|
|
|
55 |
|
4606 |
std::unique_ptr<uv_thread_t> Start() { |
56 |
|
13818 |
auto start_thread = [](void* data) { |
57 |
|
4606 |
static_cast<DelayedTaskScheduler*>(data)->Run(); |
58 |
|
13811 |
}; |
59 |
|
4606 |
std::unique_ptr<uv_thread_t> t { new uv_thread_t() }; |
60 |
|
4606 |
uv_sem_init(&ready_, 0); |
61 |
✗✓ |
4606 |
CHECK_EQ(0, uv_thread_create(t.get(), start_thread, this)); |
62 |
|
4606 |
uv_sem_wait(&ready_); |
63 |
|
4606 |
uv_sem_destroy(&ready_); |
64 |
|
4606 |
return t; |
65 |
|
|
} |
66 |
|
|
|
67 |
|
452 |
void PostDelayedTask(std::unique_ptr<Task> task, double delay_in_seconds) { |
68 |
|
904 |
tasks_.Push(std::make_unique<ScheduleTask>(this, std::move(task), |
69 |
|
452 |
delay_in_seconds)); |
70 |
|
452 |
uv_async_send(&flush_tasks_); |
71 |
|
452 |
} |
72 |
|
|
|
73 |
|
4599 |
void Stop() { |
74 |
|
4599 |
tasks_.Push(std::make_unique<StopTask>(this)); |
75 |
|
4599 |
uv_async_send(&flush_tasks_); |
76 |
|
4599 |
} |
77 |
|
|
|
78 |
|
|
private: |
79 |
|
4606 |
void Run() { |
80 |
✓✓ |
4606 |
TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", |
81 |
|
|
"WorkerThreadsTaskRunner::DelayedTaskScheduler"); |
82 |
|
4606 |
loop_.data = this; |
83 |
✗✓ |
4606 |
CHECK_EQ(0, uv_loop_init(&loop_)); |
84 |
✓✓ |
9212 |
flush_tasks_.data = this; |
85 |
✗✓ |
4606 |
CHECK_EQ(0, uv_async_init(&loop_, &flush_tasks_, FlushTasks)); |
86 |
|
4606 |
uv_sem_post(&ready_); |
87 |
|
|
|
88 |
|
4606 |
uv_run(&loop_, UV_RUN_DEFAULT); |
89 |
|
4599 |
CheckedUvLoopClose(&loop_); |
90 |
|
4599 |
} |
91 |
|
|
|
92 |
|
5051 |
static void FlushTasks(uv_async_t* flush_tasks) { |
93 |
|
|
DelayedTaskScheduler* scheduler = |
94 |
|
5051 |
ContainerOf(&DelayedTaskScheduler::loop_, flush_tasks->loop); |
95 |
✓✓ |
15153 |
while (std::unique_ptr<Task> task = scheduler->tasks_.Pop()) |
96 |
✓✓ |
10102 |
task->Run(); |
97 |
|
5051 |
} |
98 |
|
|
|
99 |
|
9198 |
class StopTask : public Task { |
100 |
|
|
public: |
101 |
|
4599 |
explicit StopTask(DelayedTaskScheduler* scheduler): scheduler_(scheduler) {} |
102 |
|
|
|
103 |
|
4599 |
void Run() override { |
104 |
|
9198 |
std::vector<uv_timer_t*> timers; |
105 |
✓✓ |
5020 |
for (uv_timer_t* timer : scheduler_->timers_) |
106 |
|
421 |
timers.push_back(timer); |
107 |
✓✓ |
5020 |
for (uv_timer_t* timer : timers) |
108 |
|
421 |
scheduler_->TakeTimerTask(timer); |
109 |
|
9198 |
uv_close(reinterpret_cast<uv_handle_t*>(&scheduler_->flush_tasks_), |
110 |
|
18396 |
[](uv_handle_t* handle) {}); |
111 |
|
4599 |
} |
112 |
|
|
|
113 |
|
|
private: |
114 |
|
|
DelayedTaskScheduler* scheduler_; |
115 |
|
|
}; |
116 |
|
|
|
117 |
|
904 |
class ScheduleTask : public Task { |
118 |
|
|
public: |
119 |
|
452 |
ScheduleTask(DelayedTaskScheduler* scheduler, |
120 |
|
|
std::unique_ptr<Task> task, |
121 |
|
|
double delay_in_seconds) |
122 |
|
452 |
: scheduler_(scheduler), |
123 |
|
452 |
task_(std::move(task)), |
124 |
|
904 |
delay_in_seconds_(delay_in_seconds) {} |
125 |
|
|
|
126 |
|
452 |
void Run() override { |
127 |
|
452 |
uint64_t delay_millis = llround(delay_in_seconds_ * 1000); |
128 |
|
904 |
std::unique_ptr<uv_timer_t> timer(new uv_timer_t()); |
129 |
✗✓ |
452 |
CHECK_EQ(0, uv_timer_init(&scheduler_->loop_, timer.get())); |
130 |
|
452 |
timer->data = task_.release(); |
131 |
✗✓ |
452 |
CHECK_EQ(0, uv_timer_start(timer.get(), RunTask, delay_millis, 0)); |
132 |
|
452 |
scheduler_->timers_.insert(timer.release()); |
133 |
|
452 |
} |
134 |
|
|
|
135 |
|
|
private: |
136 |
|
|
DelayedTaskScheduler* scheduler_; |
137 |
|
|
std::unique_ptr<Task> task_; |
138 |
|
|
double delay_in_seconds_; |
139 |
|
|
}; |
140 |
|
|
|
141 |
|
31 |
static void RunTask(uv_timer_t* timer) { |
142 |
|
|
DelayedTaskScheduler* scheduler = |
143 |
|
31 |
ContainerOf(&DelayedTaskScheduler::loop_, timer->loop); |
144 |
|
31 |
scheduler->pending_worker_tasks_->Push(scheduler->TakeTimerTask(timer)); |
145 |
|
31 |
} |
146 |
|
|
|
147 |
|
452 |
std::unique_ptr<Task> TakeTimerTask(uv_timer_t* timer) { |
148 |
|
452 |
std::unique_ptr<Task> task(static_cast<Task*>(timer->data)); |
149 |
|
452 |
uv_timer_stop(timer); |
150 |
|
2260 |
uv_close(reinterpret_cast<uv_handle_t*>(timer), [](uv_handle_t* handle) { |
151 |
|
452 |
delete reinterpret_cast<uv_timer_t*>(handle); |
152 |
|
1808 |
}); |
153 |
|
452 |
timers_.erase(timer); |
154 |
|
452 |
return task; |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
uv_sem_t ready_; |
158 |
|
|
TaskQueue<Task>* pending_worker_tasks_; |
159 |
|
|
|
160 |
|
|
TaskQueue<Task> tasks_; |
161 |
|
|
uv_loop_t loop_; |
162 |
|
|
uv_async_t flush_tasks_; |
163 |
|
|
std::unordered_set<uv_timer_t*> timers_; |
164 |
|
|
}; |
165 |
|
|
|
166 |
|
4606 |
WorkerThreadsTaskRunner::WorkerThreadsTaskRunner(int thread_pool_size) { |
167 |
|
9212 |
Mutex platform_workers_mutex; |
168 |
|
9212 |
ConditionVariable platform_workers_ready; |
169 |
|
|
|
170 |
|
9212 |
Mutex::ScopedLock lock(platform_workers_mutex); |
171 |
|
4606 |
int pending_platform_workers = thread_pool_size; |
172 |
|
|
|
173 |
|
9212 |
delayed_task_scheduler_ = std::make_unique<DelayedTaskScheduler>( |
174 |
|
13818 |
&pending_worker_tasks_); |
175 |
|
4606 |
threads_.push_back(delayed_task_scheduler_->Start()); |
176 |
|
|
|
177 |
✓✓ |
23036 |
for (int i = 0; i < thread_pool_size; i++) { |
178 |
|
|
PlatformWorkerData* worker_data = new PlatformWorkerData{ |
179 |
|
18430 |
&pending_worker_tasks_, &platform_workers_mutex, |
180 |
|
|
&platform_workers_ready, &pending_platform_workers, i |
181 |
|
36860 |
}; |
182 |
|
36860 |
std::unique_ptr<uv_thread_t> t { new uv_thread_t() }; |
183 |
✗✓ |
18430 |
if (uv_thread_create(t.get(), PlatformWorkerThread, |
184 |
|
|
worker_data) != 0) { |
185 |
|
|
break; |
186 |
|
|
} |
187 |
✓✗ |
18430 |
threads_.push_back(std::move(t)); |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
// Wait for platform workers to initialize before continuing with the |
191 |
|
|
// bootstrap. |
192 |
✓✓ |
35104 |
while (pending_platform_workers > 0) { |
193 |
|
15249 |
platform_workers_ready.Wait(lock); |
194 |
|
|
} |
195 |
|
4606 |
} |
196 |
|
|
|
197 |
|
50412 |
void WorkerThreadsTaskRunner::PostTask(std::unique_ptr<Task> task) { |
198 |
|
50412 |
pending_worker_tasks_.Push(std::move(task)); |
199 |
|
50412 |
} |
200 |
|
|
|
201 |
|
452 |
void WorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task, |
202 |
|
|
double delay_in_seconds) { |
203 |
|
452 |
delayed_task_scheduler_->PostDelayedTask(std::move(task), delay_in_seconds); |
204 |
|
452 |
} |
205 |
|
|
|
206 |
|
15629 |
void WorkerThreadsTaskRunner::BlockingDrain() { |
207 |
|
15629 |
pending_worker_tasks_.BlockingDrain(); |
208 |
|
15629 |
} |
209 |
|
|
|
210 |
|
4599 |
void WorkerThreadsTaskRunner::Shutdown() { |
211 |
|
4599 |
pending_worker_tasks_.Stop(); |
212 |
|
4599 |
delayed_task_scheduler_->Stop(); |
213 |
✓✓ |
27600 |
for (size_t i = 0; i < threads_.size(); i++) { |
214 |
✗✓ |
23001 |
CHECK_EQ(0, uv_thread_join(threads_[i].get())); |
215 |
|
|
} |
216 |
|
4599 |
} |
217 |
|
|
|
218 |
|
4840 |
int WorkerThreadsTaskRunner::NumberOfWorkerThreads() const { |
219 |
|
4840 |
return threads_.size(); |
220 |
|
|
} |
221 |
|
|
|
222 |
|
5053 |
PerIsolatePlatformData::PerIsolatePlatformData( |
223 |
|
5053 |
Isolate* isolate, uv_loop_t* loop) |
224 |
|
5053 |
: isolate_(isolate), loop_(loop) { |
225 |
|
5053 |
flush_tasks_ = new uv_async_t(); |
226 |
✗✓ |
5053 |
CHECK_EQ(0, uv_async_init(loop, flush_tasks_, FlushTasks)); |
227 |
|
5053 |
flush_tasks_->data = static_cast<void*>(this); |
228 |
|
5053 |
uv_unref(reinterpret_cast<uv_handle_t*>(flush_tasks_)); |
229 |
|
5053 |
} |
230 |
|
|
|
231 |
|
|
std::shared_ptr<v8::TaskRunner> |
232 |
|
35421 |
PerIsolatePlatformData::GetForegroundTaskRunner() { |
233 |
|
35421 |
return shared_from_this(); |
234 |
|
|
} |
235 |
|
|
|
236 |
|
10064 |
void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) { |
237 |
|
10064 |
auto platform_data = static_cast<PerIsolatePlatformData*>(handle->data); |
238 |
|
10064 |
platform_data->FlushForegroundTasksInternal(); |
239 |
|
10064 |
} |
240 |
|
|
|
241 |
|
|
void PerIsolatePlatformData::PostIdleTask(std::unique_ptr<v8::IdleTask> task) { |
242 |
|
|
UNREACHABLE(); |
243 |
|
|
} |
244 |
|
|
|
245 |
|
15364 |
void PerIsolatePlatformData::PostTask(std::unique_ptr<Task> task) { |
246 |
✗✓ |
15364 |
if (flush_tasks_ == nullptr) { |
247 |
|
|
// V8 may post tasks during Isolate disposal. In that case, the only |
248 |
|
|
// sensible path forward is to discard the task. |
249 |
|
|
return; |
250 |
|
|
} |
251 |
|
15364 |
foreground_tasks_.Push(std::move(task)); |
252 |
|
15364 |
uv_async_send(flush_tasks_); |
253 |
|
|
} |
254 |
|
|
|
255 |
|
3337 |
void PerIsolatePlatformData::PostDelayedTask( |
256 |
|
|
std::unique_ptr<Task> task, double delay_in_seconds) { |
257 |
✗✓ |
3337 |
if (flush_tasks_ == nullptr) { |
258 |
|
|
// V8 may post tasks during Isolate disposal. In that case, the only |
259 |
|
|
// sensible path forward is to discard the task. |
260 |
|
|
return; |
261 |
|
|
} |
262 |
|
6674 |
std::unique_ptr<DelayedTask> delayed(new DelayedTask()); |
263 |
|
3337 |
delayed->task = std::move(task); |
264 |
|
3337 |
delayed->platform_data = shared_from_this(); |
265 |
|
3337 |
delayed->timeout = delay_in_seconds; |
266 |
|
3337 |
foreground_delayed_tasks_.Push(std::move(delayed)); |
267 |
|
3337 |
uv_async_send(flush_tasks_); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
9070 |
void PerIsolatePlatformData::PostNonNestableTask(std::unique_ptr<Task> task) { |
271 |
|
9070 |
PostTask(std::move(task)); |
272 |
|
9070 |
} |
273 |
|
|
|
274 |
|
|
void PerIsolatePlatformData::PostNonNestableDelayedTask( |
275 |
|
|
std::unique_ptr<Task> task, |
276 |
|
|
double delay_in_seconds) { |
277 |
|
|
PostDelayedTask(std::move(task), delay_in_seconds); |
278 |
|
|
} |
279 |
|
|
|
280 |
|
918 |
PerIsolatePlatformData::~PerIsolatePlatformData() { |
281 |
✗✓ |
459 |
CHECK(!flush_tasks_); |
282 |
|
459 |
} |
283 |
|
|
|
284 |
|
405 |
void PerIsolatePlatformData::AddShutdownCallback(void (*callback)(void*), |
285 |
|
|
void* data) { |
286 |
|
405 |
shutdown_callbacks_.emplace_back(ShutdownCallback { callback, data }); |
287 |
|
405 |
} |
288 |
|
|
|
289 |
|
4466 |
void PerIsolatePlatformData::Shutdown() { |
290 |
✗✓ |
4466 |
if (flush_tasks_ == nullptr) |
291 |
|
|
return; |
292 |
|
|
|
293 |
|
|
// While there should be no V8 tasks in the queues at this point, it is |
294 |
|
|
// possible that Node.js-internal tasks from e.g. the inspector are still |
295 |
|
|
// lying around. We clear these queues and ignore the return value, |
296 |
|
|
// effectively deleting the tasks instead of running them. |
297 |
|
4466 |
foreground_delayed_tasks_.PopAll(); |
298 |
|
4466 |
foreground_tasks_.PopAll(); |
299 |
|
4466 |
scheduled_delayed_tasks_.clear(); |
300 |
|
|
|
301 |
|
|
// Both destroying the scheduled_delayed_tasks_ lists and closing |
302 |
|
|
// flush_tasks_ handle add tasks to the event loop. We keep a count of all |
303 |
|
|
// non-closed handles, and when that reaches zero, we inform any shutdown |
304 |
|
|
// callbacks that the platform is done as far as this Isolate is concerned. |
305 |
|
4466 |
self_reference_ = shared_from_this(); |
306 |
|
8932 |
uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_), |
307 |
|
5384 |
[](uv_handle_t* handle) { |
308 |
|
|
std::unique_ptr<uv_async_t> flush_tasks { |
309 |
|
918 |
reinterpret_cast<uv_async_t*>(handle) }; |
310 |
|
|
PerIsolatePlatformData* platform_data = |
311 |
|
459 |
static_cast<PerIsolatePlatformData*>(flush_tasks->data); |
312 |
|
459 |
platform_data->DecreaseHandleCount(); |
313 |
|
459 |
platform_data->self_reference_.reset(); |
314 |
|
9850 |
}); |
315 |
|
4466 |
flush_tasks_ = nullptr; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
888 |
void PerIsolatePlatformData::DecreaseHandleCount() { |
319 |
✗✓ |
888 |
CHECK_GE(uv_handle_count_, 1); |
320 |
✓✓ |
888 |
if (--uv_handle_count_ == 0) { |
321 |
✓✓ |
864 |
for (const auto& callback : shutdown_callbacks_) |
322 |
|
405 |
callback.cb(callback.data); |
323 |
|
|
} |
324 |
|
888 |
} |
325 |
|
|
|
326 |
|
4606 |
NodePlatform::NodePlatform(int thread_pool_size, |
327 |
|
4606 |
v8::TracingController* tracing_controller) { |
328 |
✓✓ |
4606 |
if (tracing_controller != nullptr) { |
329 |
|
4599 |
tracing_controller_ = tracing_controller; |
330 |
|
|
} else { |
331 |
|
7 |
tracing_controller_ = new v8::TracingController(); |
332 |
|
|
} |
333 |
|
|
// TODO(addaleax): It's a bit icky that we use global state here, but we can't |
334 |
|
|
// really do anything about it unless V8 starts exposing a way to access the |
335 |
|
|
// current v8::Platform instance. |
336 |
|
4606 |
SetTracingController(tracing_controller_); |
337 |
|
|
DCHECK_EQ(GetTracingController(), tracing_controller_); |
338 |
|
|
worker_thread_task_runner_ = |
339 |
|
4606 |
std::make_shared<WorkerThreadsTaskRunner>(thread_pool_size); |
340 |
|
4606 |
} |
341 |
|
|
|
342 |
|
13797 |
NodePlatform::~NodePlatform() { |
343 |
|
4599 |
Shutdown(); |
344 |
|
9198 |
} |
345 |
|
|
|
346 |
|
5052 |
void NodePlatform::RegisterIsolate(Isolate* isolate, uv_loop_t* loop) { |
347 |
|
10104 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
348 |
|
10104 |
auto delegate = std::make_shared<PerIsolatePlatformData>(isolate, loop); |
349 |
|
5052 |
IsolatePlatformDelegate* ptr = delegate.get(); |
350 |
|
|
auto insertion = per_isolate_.emplace( |
351 |
|
|
isolate, |
352 |
|
5052 |
std::make_pair(ptr, std::move(delegate))); |
353 |
✗✓ |
5052 |
CHECK(insertion.second); |
354 |
|
5052 |
} |
355 |
|
|
|
356 |
|
1 |
void NodePlatform::RegisterIsolate(Isolate* isolate, |
357 |
|
|
IsolatePlatformDelegate* delegate) { |
358 |
|
2 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
359 |
|
|
auto insertion = per_isolate_.emplace( |
360 |
|
|
isolate, |
361 |
|
1 |
std::make_pair(delegate, std::shared_ptr<PerIsolatePlatformData>{})); |
362 |
✗✓ |
1 |
CHECK(insertion.second); |
363 |
|
1 |
} |
364 |
|
|
|
365 |
|
4466 |
void NodePlatform::UnregisterIsolate(Isolate* isolate) { |
366 |
|
8932 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
367 |
|
4466 |
auto existing_it = per_isolate_.find(isolate); |
368 |
✗✓ |
4466 |
CHECK_NE(existing_it, per_isolate_.end()); |
369 |
|
4466 |
auto& existing = existing_it->second; |
370 |
✓✓ |
4466 |
if (existing.second) { |
371 |
|
4465 |
existing.second->Shutdown(); |
372 |
|
|
} |
373 |
|
4466 |
per_isolate_.erase(existing_it); |
374 |
|
4466 |
} |
375 |
|
|
|
376 |
|
405 |
void NodePlatform::AddIsolateFinishedCallback(Isolate* isolate, |
377 |
|
|
void (*cb)(void*), void* data) { |
378 |
|
810 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
379 |
|
405 |
auto it = per_isolate_.find(isolate); |
380 |
✗✓ |
405 |
if (it == per_isolate_.end()) { |
381 |
|
|
cb(data); |
382 |
|
|
return; |
383 |
|
|
} |
384 |
✗✓ |
405 |
CHECK(it->second.second); |
385 |
✓✗ |
405 |
it->second.second->AddShutdownCallback(cb, data); |
386 |
|
|
} |
387 |
|
|
|
388 |
|
9193 |
void NodePlatform::Shutdown() { |
389 |
✓✓ |
9193 |
if (has_shut_down_) return; |
390 |
|
4599 |
has_shut_down_ = true; |
391 |
|
4599 |
worker_thread_task_runner_->Shutdown(); |
392 |
|
|
|
393 |
|
|
{ |
394 |
|
9198 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
395 |
|
4599 |
per_isolate_.clear(); |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
|
399 |
|
4840 |
int NodePlatform::NumberOfWorkerThreads() { |
400 |
|
4840 |
return worker_thread_task_runner_->NumberOfWorkerThreads(); |
401 |
|
|
} |
402 |
|
|
|
403 |
|
14898 |
void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr<Task> task) { |
404 |
|
14898 |
DebugSealHandleScope scope(isolate_); |
405 |
|
14898 |
Environment* env = Environment::GetCurrent(isolate_); |
406 |
✓✓ |
14898 |
if (env != nullptr) { |
407 |
|
25668 |
v8::HandleScope scope(isolate_); |
408 |
|
12837 |
InternalCallbackScope cb_scope(env, Object::New(isolate_), { 0, 0 }, |
409 |
|
38511 |
InternalCallbackScope::kNoFlags); |
410 |
|
12837 |
task->Run(); |
411 |
|
|
} else { |
412 |
|
|
// The task is moved out of InternalCallbackScope if env is not available. |
413 |
|
|
// This is a required else block, and should not be removed. |
414 |
|
|
// See comment: https://github.com/nodejs/node/pull/34688#pullrequestreview-463867489 |
415 |
|
2061 |
task->Run(); |
416 |
|
|
} |
417 |
|
14892 |
} |
418 |
|
|
|
419 |
|
2 |
void PerIsolatePlatformData::DeleteFromScheduledTasks(DelayedTask* task) { |
420 |
|
|
auto it = std::find_if(scheduled_delayed_tasks_.begin(), |
421 |
|
|
scheduled_delayed_tasks_.end(), |
422 |
|
4 |
[task](const DelayedTaskPointer& delayed) -> bool { |
423 |
|
4 |
return delayed.get() == task; |
424 |
|
2 |
}); |
425 |
✗✓ |
2 |
CHECK_NE(it, scheduled_delayed_tasks_.end()); |
426 |
|
2 |
scheduled_delayed_tasks_.erase(it); |
427 |
|
2 |
} |
428 |
|
|
|
429 |
|
2 |
void PerIsolatePlatformData::RunForegroundTask(uv_timer_t* handle) { |
430 |
|
2 |
DelayedTask* delayed = ContainerOf(&DelayedTask::timer, handle); |
431 |
|
2 |
delayed->platform_data->RunForegroundTask(std::move(delayed->task)); |
432 |
|
2 |
delayed->platform_data->DeleteFromScheduledTasks(delayed); |
433 |
|
2 |
} |
434 |
|
|
|
435 |
|
8834 |
void NodePlatform::DrainTasks(Isolate* isolate) { |
436 |
|
17661 |
std::shared_ptr<PerIsolatePlatformData> per_isolate = ForNodeIsolate(isolate); |
437 |
✓✓ |
8834 |
if (!per_isolate) return; |
438 |
|
|
|
439 |
✓✓✓✓
|
15629 |
do { |
440 |
|
|
// Worker tasks aren't associated with an Isolate. |
441 |
|
15629 |
worker_thread_task_runner_->BlockingDrain(); |
442 |
|
15629 |
} while (per_isolate->FlushForegroundTasksInternal()); |
443 |
|
|
} |
444 |
|
|
|
445 |
|
25697 |
bool PerIsolatePlatformData::FlushForegroundTasksInternal() { |
446 |
|
25697 |
bool did_work = false; |
447 |
|
|
|
448 |
|
|
while (std::unique_ptr<DelayedTask> delayed = |
449 |
✓✓ |
32277 |
foreground_delayed_tasks_.Pop()) { |
450 |
|
3290 |
did_work = true; |
451 |
|
3290 |
uint64_t delay_millis = llround(delayed->timeout * 1000); |
452 |
|
|
|
453 |
|
3290 |
delayed->timer.data = static_cast<void*>(delayed.get()); |
454 |
|
3290 |
uv_timer_init(loop_, &delayed->timer); |
455 |
|
|
// Timers may not guarantee queue ordering of events with the same delay if |
456 |
|
|
// the delay is non-zero. This should not be a problem in practice. |
457 |
|
3290 |
uv_timer_start(&delayed->timer, RunForegroundTask, delay_millis, 0); |
458 |
|
3290 |
uv_unref(reinterpret_cast<uv_handle_t*>(&delayed->timer)); |
459 |
|
3290 |
uv_handle_count_++; |
460 |
|
|
|
461 |
✓✓ |
6580 |
scheduled_delayed_tasks_.emplace_back(delayed.release(), |
462 |
|
9716 |
[](DelayedTask* delayed) { |
463 |
|
6426 |
uv_close(reinterpret_cast<uv_handle_t*>(&delayed->timer), |
464 |
|
4071 |
[](uv_handle_t* handle) { |
465 |
|
|
std::unique_ptr<DelayedTask> task { |
466 |
|
858 |
static_cast<DelayedTask*>(handle->data) }; |
467 |
|
429 |
task->platform_data->DecreaseHandleCount(); |
468 |
|
7284 |
}); |
469 |
|
13006 |
}); |
470 |
|
3290 |
} |
471 |
|
|
// Move all foreground tasks into a separate queue and flush that queue. |
472 |
|
|
// This way tasks that are posted while flushing the queue will be run on the |
473 |
|
|
// next call of FlushForegroundTasksInternal. |
474 |
|
51387 |
std::queue<std::unique_ptr<Task>> tasks = foreground_tasks_.PopAll(); |
475 |
✓✓ |
55477 |
while (!tasks.empty()) { |
476 |
|
29786 |
std::unique_ptr<Task> task = std::move(tasks.front()); |
477 |
|
14896 |
tasks.pop(); |
478 |
|
14896 |
did_work = true; |
479 |
|
14896 |
RunForegroundTask(std::move(task)); |
480 |
|
|
} |
481 |
|
51381 |
return did_work; |
482 |
|
|
} |
483 |
|
|
|
484 |
|
50412 |
void NodePlatform::CallOnWorkerThread(std::unique_ptr<Task> task) { |
485 |
|
50412 |
worker_thread_task_runner_->PostTask(std::move(task)); |
486 |
|
50412 |
} |
487 |
|
|
|
488 |
|
452 |
void NodePlatform::CallDelayedOnWorkerThread(std::unique_ptr<Task> task, |
489 |
|
|
double delay_in_seconds) { |
490 |
|
904 |
worker_thread_task_runner_->PostDelayedTask(std::move(task), |
491 |
|
452 |
delay_in_seconds); |
492 |
|
452 |
} |
493 |
|
|
|
494 |
|
|
|
495 |
|
35421 |
IsolatePlatformDelegate* NodePlatform::ForIsolate(Isolate* isolate) { |
496 |
|
70842 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
497 |
|
70842 |
auto data = per_isolate_[isolate]; |
498 |
✗✓ |
35421 |
CHECK_NOT_NULL(data.first); |
499 |
|
70842 |
return data.first; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
std::shared_ptr<PerIsolatePlatformData> |
503 |
|
8838 |
NodePlatform::ForNodeIsolate(Isolate* isolate) { |
504 |
|
17676 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
505 |
|
17676 |
auto data = per_isolate_[isolate]; |
506 |
✗✓ |
8838 |
CHECK_NOT_NULL(data.first); |
507 |
|
17676 |
return data.second; |
508 |
|
|
} |
509 |
|
|
|
510 |
|
4 |
bool NodePlatform::FlushForegroundTasks(Isolate* isolate) { |
511 |
|
8 |
std::shared_ptr<PerIsolatePlatformData> per_isolate = ForNodeIsolate(isolate); |
512 |
✗✓ |
4 |
if (!per_isolate) return false; |
513 |
|
4 |
return per_isolate->FlushForegroundTasksInternal(); |
514 |
|
|
} |
515 |
|
|
|
516 |
|
89 |
std::unique_ptr<v8::JobHandle> NodePlatform::PostJob(v8::TaskPriority priority, |
517 |
|
|
std::unique_ptr<v8::JobTask> job_task) { |
518 |
|
|
return v8::platform::NewDefaultJobHandle( |
519 |
|
89 |
this, priority, std::move(job_task), NumberOfWorkerThreads()); |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { |
523 |
|
|
return ForIsolate(isolate)->IdleTasksEnabled(); |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
std::shared_ptr<v8::TaskRunner> |
527 |
|
35421 |
NodePlatform::GetForegroundTaskRunner(Isolate* isolate) { |
528 |
|
35421 |
return ForIsolate(isolate)->GetForegroundTaskRunner(); |
529 |
|
|
} |
530 |
|
|
|
531 |
|
1137785 |
double NodePlatform::MonotonicallyIncreasingTime() { |
532 |
|
|
// Convert nanos to seconds. |
533 |
|
1137785 |
return uv_hrtime() / 1e9; |
534 |
|
|
} |
535 |
|
|
|
536 |
|
15600210 |
double NodePlatform::CurrentClockTimeMillis() { |
537 |
|
15600210 |
return SystemClockTimeMillis(); |
538 |
|
|
} |
539 |
|
|
|
540 |
|
290444 |
v8::TracingController* NodePlatform::GetTracingController() { |
541 |
✗✓ |
290444 |
CHECK_NOT_NULL(tracing_controller_); |
542 |
|
290444 |
return tracing_controller_; |
543 |
|
|
} |
544 |
|
|
|
545 |
|
4606 |
Platform::StackTracePrinter NodePlatform::GetStackTracePrinter() { |
546 |
|
4606 |
return []() { |
547 |
|
|
fprintf(stderr, "\n"); |
548 |
|
|
DumpBacktrace(stderr); |
549 |
|
|
fflush(stderr); |
550 |
|
9212 |
}; |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
template <class T> |
554 |
|
19318 |
TaskQueue<T>::TaskQueue() |
555 |
|
|
: lock_(), tasks_available_(), tasks_drained_(), |
556 |
|
19318 |
outstanding_tasks_(0), stopped_(false), task_queue_() { } |
557 |
|
|
|
558 |
|
|
template <class T> |
559 |
|
74195 |
void TaskQueue<T>::Push(std::unique_ptr<T> task) { |
560 |
|
148390 |
Mutex::ScopedLock scoped_lock(lock_); |
561 |
|
74195 |
outstanding_tasks_++; |
562 |
|
74195 |
task_queue_.push(std::move(task)); |
563 |
|
74195 |
tasks_available_.Signal(scoped_lock); |
564 |
|
74195 |
} |
565 |
|
|
|
566 |
|
|
template <class T> |
567 |
|
39089 |
std::unique_ptr<T> TaskQueue<T>::Pop() { |
568 |
|
78178 |
Mutex::ScopedLock scoped_lock(lock_); |
569 |
✓✓✓✓
|
39089 |
if (task_queue_.empty()) { |
570 |
|
30748 |
return std::unique_ptr<T>(nullptr); |
571 |
|
|
} |
572 |
|
16682 |
std::unique_ptr<T> result = std::move(task_queue_.front()); |
573 |
|
8341 |
task_queue_.pop(); |
574 |
|
8341 |
return result; |
575 |
|
|
} |
576 |
|
|
|
577 |
|
|
template <class T> |
578 |
|
68860 |
std::unique_ptr<T> TaskQueue<T>::BlockingPop() { |
579 |
|
137704 |
Mutex::ScopedLock scoped_lock(lock_); |
580 |
✓✓✓✓ ✓✓ |
194790 |
while (task_queue_.empty() && !stopped_) { |
581 |
|
62987 |
tasks_available_.Wait(scoped_lock); |
582 |
|
|
} |
583 |
✓✓ |
68844 |
if (stopped_) { |
584 |
|
18402 |
return std::unique_ptr<T>(nullptr); |
585 |
|
|
} |
586 |
|
100884 |
std::unique_ptr<T> result = std::move(task_queue_.front()); |
587 |
|
50442 |
task_queue_.pop(); |
588 |
|
50442 |
return result; |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
template <class T> |
592 |
|
50376 |
void TaskQueue<T>::NotifyOfCompletion() { |
593 |
|
100818 |
Mutex::ScopedLock scoped_lock(lock_); |
594 |
✓✓ |
50442 |
if (--outstanding_tasks_ == 0) { |
595 |
|
29972 |
tasks_drained_.Broadcast(scoped_lock); |
596 |
|
|
} |
597 |
|
50435 |
} |
598 |
|
|
|
599 |
|
|
template <class T> |
600 |
|
15629 |
void TaskQueue<T>::BlockingDrain() { |
601 |
|
31258 |
Mutex::ScopedLock scoped_lock(lock_); |
602 |
✓✓ |
17409 |
while (outstanding_tasks_ > 0) { |
603 |
|
890 |
tasks_drained_.Wait(scoped_lock); |
604 |
|
|
} |
605 |
|
15629 |
} |
606 |
|
|
|
607 |
|
|
template <class T> |
608 |
|
4599 |
void TaskQueue<T>::Stop() { |
609 |
|
9198 |
Mutex::ScopedLock scoped_lock(lock_); |
610 |
|
4599 |
stopped_ = true; |
611 |
|
4599 |
tasks_available_.Broadcast(scoped_lock); |
612 |
|
4599 |
} |
613 |
|
|
|
614 |
|
|
template <class T> |
615 |
|
34629 |
std::queue<std::unique_ptr<T>> TaskQueue<T>::PopAll() { |
616 |
|
69257 |
Mutex::ScopedLock scoped_lock(lock_); |
617 |
|
34629 |
std::queue<std::unique_ptr<T>> result; |
618 |
|
34629 |
result.swap(task_queue_); |
619 |
|
69257 |
return result; |
620 |
|
|
} |
621 |
|
|
|
622 |
✓✗✓✗
|
13995 |
} // namespace node |