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 |
|
23017 |
static void PlatformWorkerThread(void* data) { |
28 |
|
|
std::unique_ptr<PlatformWorkerData> |
29 |
|
45994 |
worker_data(static_cast<PlatformWorkerData*>(data)); |
30 |
|
|
|
31 |
|
23017 |
TaskQueue<Task>* pending_worker_tasks = worker_data->task_queue; |
32 |
✓✓✓✓
|
29160 |
TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", |
33 |
|
|
"PlatformWorkerThread"); |
34 |
|
|
|
35 |
|
|
// Notify the main thread that the platform worker is ready. |
36 |
|
|
{ |
37 |
|
46034 |
Mutex::ScopedLock lock(*worker_data->platform_workers_mutex); |
38 |
|
23017 |
(*worker_data->pending_platform_workers)--; |
39 |
|
23017 |
worker_data->platform_workers_ready->Signal(lock); |
40 |
|
|
} |
41 |
|
|
|
42 |
✓✓ |
126005 |
while (std::unique_ptr<Task> task = pending_worker_tasks->BlockingPop()) { |
43 |
|
102988 |
task->Run(); |
44 |
|
102988 |
pending_worker_tasks->NotifyOfCompletion(); |
45 |
|
102988 |
} |
46 |
|
22977 |
} |
47 |
|
|
|
48 |
|
5732 |
static int GetActualThreadPoolSize(int thread_pool_size) { |
49 |
✓✓ |
5732 |
if (thread_pool_size < 1) { |
50 |
|
1 |
thread_pool_size = uv_available_parallelism() - 1; |
51 |
|
|
} |
52 |
|
5732 |
return std::max(thread_pool_size, 1); |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
} // namespace |
56 |
|
|
|
57 |
|
|
class WorkerThreadsTaskRunner::DelayedTaskScheduler { |
58 |
|
|
public: |
59 |
|
5732 |
explicit DelayedTaskScheduler(TaskQueue<Task>* tasks) |
60 |
|
5732 |
: pending_worker_tasks_(tasks) {} |
61 |
|
|
|
62 |
|
5732 |
std::unique_ptr<uv_thread_t> Start() { |
63 |
|
5732 |
auto start_thread = [](void* data) { |
64 |
|
5732 |
static_cast<DelayedTaskScheduler*>(data)->Run(); |
65 |
|
5722 |
}; |
66 |
|
5732 |
std::unique_ptr<uv_thread_t> t { new uv_thread_t() }; |
67 |
|
5732 |
uv_sem_init(&ready_, 0); |
68 |
✗✓ |
5732 |
CHECK_EQ(0, uv_thread_create(t.get(), start_thread, this)); |
69 |
|
5732 |
uv_sem_wait(&ready_); |
70 |
|
5732 |
uv_sem_destroy(&ready_); |
71 |
|
5732 |
return t; |
72 |
|
|
} |
73 |
|
|
|
74 |
|
526 |
void PostDelayedTask(std::unique_ptr<Task> task, double delay_in_seconds) { |
75 |
|
526 |
tasks_.Push(std::make_unique<ScheduleTask>(this, std::move(task), |
76 |
|
|
delay_in_seconds)); |
77 |
|
526 |
uv_async_send(&flush_tasks_); |
78 |
|
526 |
} |
79 |
|
|
|
80 |
|
5722 |
void Stop() { |
81 |
|
5722 |
tasks_.Push(std::make_unique<StopTask>(this)); |
82 |
|
5722 |
uv_async_send(&flush_tasks_); |
83 |
|
5722 |
} |
84 |
|
|
|
85 |
|
|
private: |
86 |
|
5732 |
void Run() { |
87 |
✓✗✓✓
|
11550 |
TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", |
88 |
|
|
"WorkerThreadsTaskRunner::DelayedTaskScheduler"); |
89 |
|
5732 |
loop_.data = this; |
90 |
✗✓ |
5732 |
CHECK_EQ(0, uv_loop_init(&loop_)); |
91 |
|
5732 |
flush_tasks_.data = this; |
92 |
✗✓ |
5732 |
CHECK_EQ(0, uv_async_init(&loop_, &flush_tasks_, FlushTasks)); |
93 |
|
5732 |
uv_sem_post(&ready_); |
94 |
|
|
|
95 |
|
5732 |
uv_run(&loop_, UV_RUN_DEFAULT); |
96 |
|
5722 |
CheckedUvLoopClose(&loop_); |
97 |
|
5722 |
} |
98 |
|
|
|
99 |
|
6248 |
static void FlushTasks(uv_async_t* flush_tasks) { |
100 |
|
|
DelayedTaskScheduler* scheduler = |
101 |
|
6248 |
ContainerOf(&DelayedTaskScheduler::loop_, flush_tasks->loop); |
102 |
✓✓ |
12496 |
while (std::unique_ptr<Task> task = scheduler->tasks_.Pop()) |
103 |
|
12496 |
task->Run(); |
104 |
|
6248 |
} |
105 |
|
|
|
106 |
|
|
class StopTask : public Task { |
107 |
|
|
public: |
108 |
|
5722 |
explicit StopTask(DelayedTaskScheduler* scheduler): scheduler_(scheduler) {} |
109 |
|
|
|
110 |
|
5722 |
void Run() override { |
111 |
|
5722 |
std::vector<uv_timer_t*> timers; |
112 |
✓✓ |
6004 |
for (uv_timer_t* timer : scheduler_->timers_) |
113 |
|
282 |
timers.push_back(timer); |
114 |
✓✓ |
6004 |
for (uv_timer_t* timer : timers) |
115 |
|
282 |
scheduler_->TakeTimerTask(timer); |
116 |
|
5722 |
uv_close(reinterpret_cast<uv_handle_t*>(&scheduler_->flush_tasks_), |
117 |
|
5722 |
[](uv_handle_t* handle) {}); |
118 |
|
5722 |
} |
119 |
|
|
|
120 |
|
|
private: |
121 |
|
|
DelayedTaskScheduler* scheduler_; |
122 |
|
|
}; |
123 |
|
|
|
124 |
|
|
class ScheduleTask : public Task { |
125 |
|
|
public: |
126 |
|
526 |
ScheduleTask(DelayedTaskScheduler* scheduler, |
127 |
|
|
std::unique_ptr<Task> task, |
128 |
|
|
double delay_in_seconds) |
129 |
|
526 |
: scheduler_(scheduler), |
130 |
|
526 |
task_(std::move(task)), |
131 |
|
526 |
delay_in_seconds_(delay_in_seconds) {} |
132 |
|
|
|
133 |
|
526 |
void Run() override { |
134 |
|
526 |
uint64_t delay_millis = llround(delay_in_seconds_ * 1000); |
135 |
|
526 |
std::unique_ptr<uv_timer_t> timer(new uv_timer_t()); |
136 |
✗✓ |
526 |
CHECK_EQ(0, uv_timer_init(&scheduler_->loop_, timer.get())); |
137 |
|
526 |
timer->data = task_.release(); |
138 |
✗✓ |
526 |
CHECK_EQ(0, uv_timer_start(timer.get(), RunTask, delay_millis, 0)); |
139 |
|
526 |
scheduler_->timers_.insert(timer.release()); |
140 |
|
526 |
} |
141 |
|
|
|
142 |
|
|
private: |
143 |
|
|
DelayedTaskScheduler* scheduler_; |
144 |
|
|
std::unique_ptr<Task> task_; |
145 |
|
|
double delay_in_seconds_; |
146 |
|
|
}; |
147 |
|
|
|
148 |
|
244 |
static void RunTask(uv_timer_t* timer) { |
149 |
|
|
DelayedTaskScheduler* scheduler = |
150 |
|
244 |
ContainerOf(&DelayedTaskScheduler::loop_, timer->loop); |
151 |
|
244 |
scheduler->pending_worker_tasks_->Push(scheduler->TakeTimerTask(timer)); |
152 |
|
244 |
} |
153 |
|
|
|
154 |
|
526 |
std::unique_ptr<Task> TakeTimerTask(uv_timer_t* timer) { |
155 |
|
526 |
std::unique_ptr<Task> task(static_cast<Task*>(timer->data)); |
156 |
|
526 |
uv_timer_stop(timer); |
157 |
|
526 |
uv_close(reinterpret_cast<uv_handle_t*>(timer), [](uv_handle_t* handle) { |
158 |
|
526 |
delete reinterpret_cast<uv_timer_t*>(handle); |
159 |
|
526 |
}); |
160 |
|
526 |
timers_.erase(timer); |
161 |
|
526 |
return task; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
uv_sem_t ready_; |
165 |
|
|
TaskQueue<Task>* pending_worker_tasks_; |
166 |
|
|
|
167 |
|
|
TaskQueue<Task> tasks_; |
168 |
|
|
uv_loop_t loop_; |
169 |
|
|
uv_async_t flush_tasks_; |
170 |
|
|
std::unordered_set<uv_timer_t*> timers_; |
171 |
|
|
}; |
172 |
|
|
|
173 |
|
5732 |
WorkerThreadsTaskRunner::WorkerThreadsTaskRunner(int thread_pool_size) { |
174 |
|
11464 |
Mutex platform_workers_mutex; |
175 |
|
11464 |
ConditionVariable platform_workers_ready; |
176 |
|
|
|
177 |
|
11464 |
Mutex::ScopedLock lock(platform_workers_mutex); |
178 |
|
5732 |
int pending_platform_workers = thread_pool_size; |
179 |
|
|
|
180 |
|
5732 |
delayed_task_scheduler_ = std::make_unique<DelayedTaskScheduler>( |
181 |
|
5732 |
&pending_worker_tasks_); |
182 |
|
5732 |
threads_.push_back(delayed_task_scheduler_->Start()); |
183 |
|
|
|
184 |
✓✓ |
28749 |
for (int i = 0; i < thread_pool_size; i++) { |
185 |
|
|
PlatformWorkerData* worker_data = new PlatformWorkerData{ |
186 |
|
23017 |
&pending_worker_tasks_, &platform_workers_mutex, |
187 |
|
|
&platform_workers_ready, &pending_platform_workers, i |
188 |
|
23017 |
}; |
189 |
|
23017 |
std::unique_ptr<uv_thread_t> t { new uv_thread_t() }; |
190 |
|
23017 |
if (uv_thread_create(t.get(), PlatformWorkerThread, |
191 |
✗✓ |
23017 |
worker_data) != 0) { |
192 |
|
|
break; |
193 |
|
|
} |
194 |
|
23017 |
threads_.push_back(std::move(t)); |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
// Wait for platform workers to initialize before continuing with the |
198 |
|
|
// bootstrap. |
199 |
✓✓ |
25066 |
while (pending_platform_workers > 0) { |
200 |
|
19334 |
platform_workers_ready.Wait(lock); |
201 |
|
|
} |
202 |
|
5732 |
} |
203 |
|
|
|
204 |
|
102744 |
void WorkerThreadsTaskRunner::PostTask(std::unique_ptr<Task> task) { |
205 |
|
102744 |
pending_worker_tasks_.Push(std::move(task)); |
206 |
|
102744 |
} |
207 |
|
|
|
208 |
|
526 |
void WorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task, |
209 |
|
|
double delay_in_seconds) { |
210 |
|
526 |
delayed_task_scheduler_->PostDelayedTask(std::move(task), delay_in_seconds); |
211 |
|
526 |
} |
212 |
|
|
|
213 |
|
16740 |
void WorkerThreadsTaskRunner::BlockingDrain() { |
214 |
|
16740 |
pending_worker_tasks_.BlockingDrain(); |
215 |
|
16740 |
} |
216 |
|
|
|
217 |
|
5722 |
void WorkerThreadsTaskRunner::Shutdown() { |
218 |
|
5722 |
pending_worker_tasks_.Stop(); |
219 |
|
5722 |
delayed_task_scheduler_->Stop(); |
220 |
✓✓ |
34421 |
for (size_t i = 0; i < threads_.size(); i++) { |
221 |
✗✓ |
28699 |
CHECK_EQ(0, uv_thread_join(threads_[i].get())); |
222 |
|
|
} |
223 |
|
5722 |
} |
224 |
|
|
|
225 |
|
48947 |
int WorkerThreadsTaskRunner::NumberOfWorkerThreads() const { |
226 |
|
48947 |
return threads_.size(); |
227 |
|
|
} |
228 |
|
|
|
229 |
|
6559 |
PerIsolatePlatformData::PerIsolatePlatformData( |
230 |
|
6559 |
Isolate* isolate, uv_loop_t* loop) |
231 |
|
6559 |
: isolate_(isolate), loop_(loop) { |
232 |
|
6559 |
flush_tasks_ = new uv_async_t(); |
233 |
✗✓ |
6559 |
CHECK_EQ(0, uv_async_init(loop, flush_tasks_, FlushTasks)); |
234 |
|
6559 |
flush_tasks_->data = static_cast<void*>(this); |
235 |
|
6559 |
uv_unref(reinterpret_cast<uv_handle_t*>(flush_tasks_)); |
236 |
|
6559 |
} |
237 |
|
|
|
238 |
|
|
std::shared_ptr<v8::TaskRunner> |
239 |
|
30917 |
PerIsolatePlatformData::GetForegroundTaskRunner() { |
240 |
|
30917 |
return shared_from_this(); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
7597 |
void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) { |
244 |
|
7597 |
auto platform_data = static_cast<PerIsolatePlatformData*>(handle->data); |
245 |
|
7597 |
platform_data->FlushForegroundTasksInternal(); |
246 |
|
7597 |
} |
247 |
|
|
|
248 |
|
|
void PerIsolatePlatformData::PostIdleTask(std::unique_ptr<v8::IdleTask> task) { |
249 |
|
|
UNREACHABLE(); |
250 |
|
|
} |
251 |
|
|
|
252 |
|
11201 |
void PerIsolatePlatformData::PostTask(std::unique_ptr<Task> task) { |
253 |
✗✓ |
11201 |
if (flush_tasks_ == nullptr) { |
254 |
|
|
// V8 may post tasks during Isolate disposal. In that case, the only |
255 |
|
|
// sensible path forward is to discard the task. |
256 |
|
|
return; |
257 |
|
|
} |
258 |
|
11201 |
foreground_tasks_.Push(std::move(task)); |
259 |
|
11201 |
uv_async_send(flush_tasks_); |
260 |
|
|
} |
261 |
|
|
|
262 |
|
6054 |
void PerIsolatePlatformData::PostDelayedTask( |
263 |
|
|
std::unique_ptr<Task> task, double delay_in_seconds) { |
264 |
✗✓ |
6054 |
if (flush_tasks_ == nullptr) { |
265 |
|
|
// V8 may post tasks during Isolate disposal. In that case, the only |
266 |
|
|
// sensible path forward is to discard the task. |
267 |
|
|
return; |
268 |
|
|
} |
269 |
|
12108 |
std::unique_ptr<DelayedTask> delayed(new DelayedTask()); |
270 |
|
6054 |
delayed->task = std::move(task); |
271 |
|
6054 |
delayed->platform_data = shared_from_this(); |
272 |
|
6054 |
delayed->timeout = delay_in_seconds; |
273 |
|
6054 |
foreground_delayed_tasks_.Push(std::move(delayed)); |
274 |
|
6054 |
uv_async_send(flush_tasks_); |
275 |
|
|
} |
276 |
|
|
|
277 |
|
10251 |
void PerIsolatePlatformData::PostNonNestableTask(std::unique_ptr<Task> task) { |
278 |
|
10251 |
PostTask(std::move(task)); |
279 |
|
10251 |
} |
280 |
|
|
|
281 |
|
|
void PerIsolatePlatformData::PostNonNestableDelayedTask( |
282 |
|
|
std::unique_ptr<Task> task, |
283 |
|
|
double delay_in_seconds) { |
284 |
|
|
PostDelayedTask(std::move(task), delay_in_seconds); |
285 |
|
|
} |
286 |
|
|
|
287 |
|
1674 |
PerIsolatePlatformData::~PerIsolatePlatformData() { |
288 |
✗✓ |
1674 |
CHECK(!flush_tasks_); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
775 |
void PerIsolatePlatformData::AddShutdownCallback(void (*callback)(void*), |
292 |
|
|
void* data) { |
293 |
|
775 |
shutdown_callbacks_.emplace_back(ShutdownCallback { callback, data }); |
294 |
|
775 |
} |
295 |
|
|
|
296 |
|
5946 |
void PerIsolatePlatformData::Shutdown() { |
297 |
✗✓ |
5946 |
if (flush_tasks_ == nullptr) |
298 |
|
|
return; |
299 |
|
|
|
300 |
|
|
// While there should be no V8 tasks in the queues at this point, it is |
301 |
|
|
// possible that Node.js-internal tasks from e.g. the inspector are still |
302 |
|
|
// lying around. We clear these queues and ignore the return value, |
303 |
|
|
// effectively deleting the tasks instead of running them. |
304 |
|
5946 |
foreground_delayed_tasks_.PopAll(); |
305 |
|
5946 |
foreground_tasks_.PopAll(); |
306 |
|
5946 |
scheduled_delayed_tasks_.clear(); |
307 |
|
|
|
308 |
|
|
// Both destroying the scheduled_delayed_tasks_ lists and closing |
309 |
|
|
// flush_tasks_ handle add tasks to the event loop. We keep a count of all |
310 |
|
|
// non-closed handles, and when that reaches zero, we inform any shutdown |
311 |
|
|
// callbacks that the platform is done as far as this Isolate is concerned. |
312 |
|
5946 |
self_reference_ = shared_from_this(); |
313 |
|
5946 |
uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_), |
314 |
|
837 |
[](uv_handle_t* handle) { |
315 |
|
|
std::unique_ptr<uv_async_t> flush_tasks { |
316 |
|
1674 |
reinterpret_cast<uv_async_t*>(handle) }; |
317 |
|
|
PerIsolatePlatformData* platform_data = |
318 |
|
837 |
static_cast<PerIsolatePlatformData*>(flush_tasks->data); |
319 |
|
837 |
platform_data->DecreaseHandleCount(); |
320 |
|
837 |
platform_data->self_reference_.reset(); |
321 |
|
837 |
}); |
322 |
|
5946 |
flush_tasks_ = nullptr; |
323 |
|
|
} |
324 |
|
|
|
325 |
|
1663 |
void PerIsolatePlatformData::DecreaseHandleCount() { |
326 |
✗✓ |
1663 |
CHECK_GE(uv_handle_count_, 1); |
327 |
✓✓ |
1663 |
if (--uv_handle_count_ == 0) { |
328 |
✓✓ |
1612 |
for (const auto& callback : shutdown_callbacks_) |
329 |
|
775 |
callback.cb(callback.data); |
330 |
|
|
} |
331 |
|
1663 |
} |
332 |
|
|
|
333 |
|
5732 |
NodePlatform::NodePlatform(int thread_pool_size, |
334 |
|
|
v8::TracingController* tracing_controller, |
335 |
|
5732 |
v8::PageAllocator* page_allocator) { |
336 |
✓✓ |
5732 |
if (tracing_controller != nullptr) { |
337 |
|
5725 |
tracing_controller_ = tracing_controller; |
338 |
|
|
} else { |
339 |
|
7 |
tracing_controller_ = new v8::TracingController(); |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
// V8 will default to its built in allocator if none is provided. |
343 |
|
5732 |
page_allocator_ = page_allocator; |
344 |
|
|
|
345 |
|
|
// TODO(addaleax): It's a bit icky that we use global state here, but we can't |
346 |
|
|
// really do anything about it unless V8 starts exposing a way to access the |
347 |
|
|
// current v8::Platform instance. |
348 |
|
5732 |
SetTracingController(tracing_controller_); |
349 |
|
|
DCHECK_EQ(GetTracingController(), tracing_controller_); |
350 |
|
|
|
351 |
|
5732 |
thread_pool_size = GetActualThreadPoolSize(thread_pool_size); |
352 |
|
|
worker_thread_task_runner_ = |
353 |
|
5732 |
std::make_shared<WorkerThreadsTaskRunner>(thread_pool_size); |
354 |
|
5732 |
} |
355 |
|
|
|
356 |
|
22888 |
NodePlatform::~NodePlatform() { |
357 |
|
11444 |
Shutdown(); |
358 |
|
22888 |
} |
359 |
|
|
|
360 |
|
6558 |
void NodePlatform::RegisterIsolate(Isolate* isolate, uv_loop_t* loop) { |
361 |
|
13116 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
362 |
|
13116 |
auto delegate = std::make_shared<PerIsolatePlatformData>(isolate, loop); |
363 |
|
6558 |
IsolatePlatformDelegate* ptr = delegate.get(); |
364 |
|
|
auto insertion = per_isolate_.emplace( |
365 |
|
|
isolate, |
366 |
|
6558 |
std::make_pair(ptr, std::move(delegate))); |
367 |
✗✓ |
6558 |
CHECK(insertion.second); |
368 |
|
6558 |
} |
369 |
|
|
|
370 |
|
1 |
void NodePlatform::RegisterIsolate(Isolate* isolate, |
371 |
|
|
IsolatePlatformDelegate* delegate) { |
372 |
|
2 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
373 |
|
|
auto insertion = per_isolate_.emplace( |
374 |
|
|
isolate, |
375 |
|
1 |
std::make_pair(delegate, std::shared_ptr<PerIsolatePlatformData>{})); |
376 |
✗✓ |
1 |
CHECK(insertion.second); |
377 |
|
1 |
} |
378 |
|
|
|
379 |
|
5946 |
void NodePlatform::UnregisterIsolate(Isolate* isolate) { |
380 |
|
11892 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
381 |
|
5946 |
auto existing_it = per_isolate_.find(isolate); |
382 |
✗✓ |
5946 |
CHECK_NE(existing_it, per_isolate_.end()); |
383 |
|
5946 |
auto& existing = existing_it->second; |
384 |
✓✓ |
5946 |
if (existing.second) { |
385 |
|
5945 |
existing.second->Shutdown(); |
386 |
|
|
} |
387 |
|
5946 |
per_isolate_.erase(existing_it); |
388 |
|
5946 |
} |
389 |
|
|
|
390 |
|
775 |
void NodePlatform::AddIsolateFinishedCallback(Isolate* isolate, |
391 |
|
|
void (*cb)(void*), void* data) { |
392 |
|
775 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
393 |
|
775 |
auto it = per_isolate_.find(isolate); |
394 |
✗✓ |
775 |
if (it == per_isolate_.end()) { |
395 |
|
|
cb(data); |
396 |
|
|
return; |
397 |
|
|
} |
398 |
✗✓ |
775 |
CHECK(it->second.second); |
399 |
|
775 |
it->second.second->AddShutdownCallback(cb, data); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
11439 |
void NodePlatform::Shutdown() { |
403 |
✓✓ |
11439 |
if (has_shut_down_) return; |
404 |
|
5722 |
has_shut_down_ = true; |
405 |
|
5722 |
worker_thread_task_runner_->Shutdown(); |
406 |
|
|
|
407 |
|
|
{ |
408 |
|
11444 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
409 |
|
5722 |
per_isolate_.clear(); |
410 |
|
|
} |
411 |
|
|
} |
412 |
|
|
|
413 |
|
48947 |
int NodePlatform::NumberOfWorkerThreads() { |
414 |
|
48947 |
return worker_thread_task_runner_->NumberOfWorkerThreads(); |
415 |
|
|
} |
416 |
|
|
|
417 |
|
10806 |
void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr<Task> task) { |
418 |
✓✓ |
10806 |
if (isolate_->IsExecutionTerminating()) return; |
419 |
|
10804 |
DebugSealHandleScope scope(isolate_); |
420 |
|
10804 |
Environment* env = Environment::GetCurrent(isolate_); |
421 |
✓✓ |
10804 |
if (env != nullptr) { |
422 |
|
16619 |
v8::HandleScope scope(isolate_); |
423 |
|
8314 |
InternalCallbackScope cb_scope(env, Object::New(isolate_), { 0, 0 }, |
424 |
|
16628 |
InternalCallbackScope::kNoFlags); |
425 |
|
8314 |
task->Run(); |
426 |
|
|
} else { |
427 |
|
|
// The task is moved out of InternalCallbackScope if env is not available. |
428 |
|
|
// This is a required else block, and should not be removed. |
429 |
|
|
// See comment: https://github.com/nodejs/node/pull/34688#pullrequestreview-463867489 |
430 |
|
2490 |
task->Run(); |
431 |
|
|
} |
432 |
|
|
} |
433 |
|
|
|
434 |
|
16 |
void PerIsolatePlatformData::DeleteFromScheduledTasks(DelayedTask* task) { |
435 |
|
|
auto it = std::find_if(scheduled_delayed_tasks_.begin(), |
436 |
|
|
scheduled_delayed_tasks_.end(), |
437 |
|
32 |
[task](const DelayedTaskPointer& delayed) -> bool { |
438 |
|
16 |
return delayed.get() == task; |
439 |
|
16 |
}); |
440 |
✗✓ |
16 |
CHECK_NE(it, scheduled_delayed_tasks_.end()); |
441 |
|
16 |
scheduled_delayed_tasks_.erase(it); |
442 |
|
16 |
} |
443 |
|
|
|
444 |
|
16 |
void PerIsolatePlatformData::RunForegroundTask(uv_timer_t* handle) { |
445 |
|
16 |
DelayedTask* delayed = ContainerOf(&DelayedTask::timer, handle); |
446 |
|
16 |
delayed->platform_data->RunForegroundTask(std::move(delayed->task)); |
447 |
|
16 |
delayed->platform_data->DeleteFromScheduledTasks(delayed); |
448 |
|
16 |
} |
449 |
|
|
|
450 |
|
11627 |
void NodePlatform::DrainTasks(Isolate* isolate) { |
451 |
|
11627 |
std::shared_ptr<PerIsolatePlatformData> per_isolate = ForNodeIsolate(isolate); |
452 |
✓✓ |
11627 |
if (!per_isolate) return; |
453 |
|
|
|
454 |
|
5114 |
do { |
455 |
|
|
// Worker tasks aren't associated with an Isolate. |
456 |
|
16740 |
worker_thread_task_runner_->BlockingDrain(); |
457 |
✓✓ |
16740 |
} while (per_isolate->FlushForegroundTasksInternal()); |
458 |
|
|
} |
459 |
|
|
|
460 |
|
24341 |
bool PerIsolatePlatformData::FlushForegroundTasksInternal() { |
461 |
|
24341 |
bool did_work = false; |
462 |
|
|
|
463 |
|
|
while (std::unique_ptr<DelayedTask> delayed = |
464 |
✓✓ |
30135 |
foreground_delayed_tasks_.Pop()) { |
465 |
|
5794 |
did_work = true; |
466 |
|
5794 |
uint64_t delay_millis = llround(delayed->timeout * 1000); |
467 |
|
|
|
468 |
|
5794 |
delayed->timer.data = static_cast<void*>(delayed.get()); |
469 |
|
5794 |
uv_timer_init(loop_, &delayed->timer); |
470 |
|
|
// Timers may not guarantee queue ordering of events with the same delay if |
471 |
|
|
// the delay is non-zero. This should not be a problem in practice. |
472 |
|
5794 |
uv_timer_start(&delayed->timer, RunForegroundTask, delay_millis, 0); |
473 |
|
5794 |
uv_unref(reinterpret_cast<uv_handle_t*>(&delayed->timer)); |
474 |
|
5794 |
uv_handle_count_++; |
475 |
|
|
|
476 |
|
17382 |
scheduled_delayed_tasks_.emplace_back(delayed.release(), |
477 |
|
5602 |
[](DelayedTask* delayed) { |
478 |
|
5602 |
uv_close(reinterpret_cast<uv_handle_t*>(&delayed->timer), |
479 |
|
826 |
[](uv_handle_t* handle) { |
480 |
|
|
std::unique_ptr<DelayedTask> task { |
481 |
|
1652 |
static_cast<DelayedTask*>(handle->data) }; |
482 |
|
826 |
task->platform_data->DecreaseHandleCount(); |
483 |
|
826 |
}); |
484 |
|
5794 |
}); |
485 |
|
5794 |
} |
486 |
|
|
// Move all foreground tasks into a separate queue and flush that queue. |
487 |
|
|
// This way tasks that are posted while flushing the queue will be run on the |
488 |
|
|
// next call of FlushForegroundTasksInternal. |
489 |
|
24341 |
std::queue<std::unique_ptr<Task>> tasks = foreground_tasks_.PopAll(); |
490 |
✓✓ |
35122 |
while (!tasks.empty()) { |
491 |
|
10790 |
std::unique_ptr<Task> task = std::move(tasks.front()); |
492 |
|
10790 |
tasks.pop(); |
493 |
|
10790 |
did_work = true; |
494 |
|
10790 |
RunForegroundTask(std::move(task)); |
495 |
|
|
} |
496 |
|
24332 |
return did_work; |
497 |
|
|
} |
498 |
|
|
|
499 |
|
102744 |
void NodePlatform::CallOnWorkerThread(std::unique_ptr<Task> task) { |
500 |
|
102744 |
worker_thread_task_runner_->PostTask(std::move(task)); |
501 |
|
102744 |
} |
502 |
|
|
|
503 |
|
526 |
void NodePlatform::CallDelayedOnWorkerThread(std::unique_ptr<Task> task, |
504 |
|
|
double delay_in_seconds) { |
505 |
|
526 |
worker_thread_task_runner_->PostDelayedTask(std::move(task), |
506 |
|
|
delay_in_seconds); |
507 |
|
526 |
} |
508 |
|
|
|
509 |
|
|
|
510 |
|
30917 |
IsolatePlatformDelegate* NodePlatform::ForIsolate(Isolate* isolate) { |
511 |
|
61834 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
512 |
|
30917 |
auto data = per_isolate_[isolate]; |
513 |
✗✓ |
30917 |
CHECK_NOT_NULL(data.first); |
514 |
|
30917 |
return data.first; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
std::shared_ptr<PerIsolatePlatformData> |
518 |
|
11631 |
NodePlatform::ForNodeIsolate(Isolate* isolate) { |
519 |
|
23262 |
Mutex::ScopedLock lock(per_isolate_mutex_); |
520 |
|
23262 |
auto data = per_isolate_[isolate]; |
521 |
✗✓ |
11631 |
CHECK_NOT_NULL(data.first); |
522 |
|
11631 |
return data.second; |
523 |
|
|
} |
524 |
|
|
|
525 |
|
4 |
bool NodePlatform::FlushForegroundTasks(Isolate* isolate) { |
526 |
|
8 |
std::shared_ptr<PerIsolatePlatformData> per_isolate = ForNodeIsolate(isolate); |
527 |
✗✓ |
4 |
if (!per_isolate) return false; |
528 |
|
4 |
return per_isolate->FlushForegroundTasksInternal(); |
529 |
|
|
} |
530 |
|
|
|
531 |
|
32899 |
std::unique_ptr<v8::JobHandle> NodePlatform::CreateJob( |
532 |
|
|
v8::TaskPriority priority, std::unique_ptr<v8::JobTask> job_task) { |
533 |
|
|
return v8::platform::NewDefaultJobHandle( |
534 |
|
32899 |
this, priority, std::move(job_task), NumberOfWorkerThreads()); |
535 |
|
|
} |
536 |
|
|
|
537 |
|
|
bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { |
538 |
|
|
return ForIsolate(isolate)->IdleTasksEnabled(); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
std::shared_ptr<v8::TaskRunner> |
542 |
|
30917 |
NodePlatform::GetForegroundTaskRunner(Isolate* isolate) { |
543 |
|
30917 |
return ForIsolate(isolate)->GetForegroundTaskRunner(); |
544 |
|
|
} |
545 |
|
|
|
546 |
|
261778 |
double NodePlatform::MonotonicallyIncreasingTime() { |
547 |
|
|
// Convert nanos to seconds. |
548 |
|
261778 |
return uv_hrtime() / 1e9; |
549 |
|
|
} |
550 |
|
|
|
551 |
|
24751934 |
double NodePlatform::CurrentClockTimeMillis() { |
552 |
|
24751934 |
return SystemClockTimeMillis(); |
553 |
|
|
} |
554 |
|
|
|
555 |
|
376679 |
v8::TracingController* NodePlatform::GetTracingController() { |
556 |
✗✓ |
376679 |
CHECK_NOT_NULL(tracing_controller_); |
557 |
|
376679 |
return tracing_controller_; |
558 |
|
|
} |
559 |
|
|
|
560 |
|
5732 |
Platform::StackTracePrinter NodePlatform::GetStackTracePrinter() { |
561 |
|
|
return []() { |
562 |
|
|
fprintf(stderr, "\n"); |
563 |
|
|
DumpBacktrace(stderr); |
564 |
|
|
fflush(stderr); |
565 |
|
5732 |
}; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
5733 |
v8::PageAllocator* NodePlatform::GetPageAllocator() { |
569 |
|
5733 |
return page_allocator_; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
template <class T> |
573 |
|
49164 |
TaskQueue<T>::TaskQueue() |
574 |
|
|
: lock_(), tasks_available_(), tasks_drained_(), |
575 |
|
49164 |
outstanding_tasks_(0), stopped_(false), task_queue_() { } |
576 |
|
|
|
577 |
|
|
template <class T> |
578 |
|
252982 |
void TaskQueue<T>::Push(std::unique_ptr<T> task) { |
579 |
|
505964 |
Mutex::ScopedLock scoped_lock(lock_); |
580 |
|
252982 |
outstanding_tasks_++; |
581 |
|
252982 |
task_queue_.push(std::move(task)); |
582 |
|
252982 |
tasks_available_.Signal(scoped_lock); |
583 |
|
252982 |
} |
584 |
|
|
|
585 |
|
|
template <class T> |
586 |
|
85262 |
std::unique_ptr<T> TaskQueue<T>::Pop() { |
587 |
|
170524 |
Mutex::ScopedLock scoped_lock(lock_); |
588 |
✓✓ |
85262 |
if (task_queue_.empty()) { |
589 |
|
61178 |
return std::unique_ptr<T>(nullptr); |
590 |
|
|
} |
591 |
|
48168 |
std::unique_ptr<T> result = std::move(task_queue_.front()); |
592 |
|
24084 |
task_queue_.pop(); |
593 |
|
24084 |
return result; |
594 |
|
|
} |
595 |
|
|
|
596 |
|
|
template <class T> |
597 |
|
126005 |
std::unique_ptr<T> TaskQueue<T>::BlockingPop() { |
598 |
|
251970 |
Mutex::ScopedLock scoped_lock(lock_); |
599 |
✓✓✓✓ ✓✓ |
240915 |
while (task_queue_.empty() && !stopped_) { |
600 |
|
114950 |
tasks_available_.Wait(scoped_lock); |
601 |
|
|
} |
602 |
✓✓ |
125965 |
if (stopped_) { |
603 |
|
22977 |
return std::unique_ptr<T>(nullptr); |
604 |
|
|
} |
605 |
|
205976 |
std::unique_ptr<T> result = std::move(task_queue_.front()); |
606 |
|
102988 |
task_queue_.pop(); |
607 |
|
102988 |
return result; |
608 |
|
|
} |
609 |
|
|
|
610 |
|
|
template <class T> |
611 |
|
102988 |
void TaskQueue<T>::NotifyOfCompletion() { |
612 |
|
205976 |
Mutex::ScopedLock scoped_lock(lock_); |
613 |
✓✓ |
102988 |
if (--outstanding_tasks_ == 0) { |
614 |
|
50404 |
tasks_drained_.Broadcast(scoped_lock); |
615 |
|
|
} |
616 |
|
102988 |
} |
617 |
|
|
|
618 |
|
|
template <class T> |
619 |
|
16740 |
void TaskQueue<T>::BlockingDrain() { |
620 |
|
33480 |
Mutex::ScopedLock scoped_lock(lock_); |
621 |
✓✓ |
18648 |
while (outstanding_tasks_ > 0) { |
622 |
|
1908 |
tasks_drained_.Wait(scoped_lock); |
623 |
|
|
} |
624 |
|
16740 |
} |
625 |
|
|
|
626 |
|
|
template <class T> |
627 |
|
5722 |
void TaskQueue<T>::Stop() { |
628 |
|
11444 |
Mutex::ScopedLock scoped_lock(lock_); |
629 |
|
5722 |
stopped_ = true; |
630 |
|
5722 |
tasks_available_.Broadcast(scoped_lock); |
631 |
|
5722 |
} |
632 |
|
|
|
633 |
|
|
template <class T> |
634 |
|
36233 |
std::queue<std::unique_ptr<T>> TaskQueue<T>::PopAll() { |
635 |
|
72466 |
Mutex::ScopedLock scoped_lock(lock_); |
636 |
|
36233 |
std::queue<std::unique_ptr<T>> result; |
637 |
|
36233 |
result.swap(task_queue_); |
638 |
|
36233 |
return result; |
639 |
|
|
} |
640 |
|
|
|
641 |
|
|
} // namespace node |