@@ -40,9 +40,10 @@ static void PlatformWorkerThread(void* data) { |
40 | 40 | worker_data->platform_workers_ready->Signal(lock); |
41 | 41 | } |
42 | 42 |
|
43 | | - while (std::unique_ptr<Task> task = pending_worker_tasks->BlockingPop()) { |
| 43 | + while (std::unique_ptr<Task> task = |
| 44 | + pending_worker_tasks->Lock().BlockingPop()) { |
44 | 45 | task->Run(); |
45 | | - pending_worker_tasks->NotifyOfCompletion(); |
| 46 | + pending_worker_tasks->Lock().NotifyOfCompletion(); |
46 | 47 | } |
47 | 48 | } |
48 | 49 |
|
@@ -73,13 +74,15 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { |
73 | 74 | } |
74 | 75 |
|
75 | 76 | void PostDelayedTask(std::unique_ptr<Task> task, double delay_in_seconds) { |
76 | | - tasks_.Push(std::make_unique<ScheduleTask>(this, std::move(task), |
77 | | - delay_in_seconds)); |
| 77 | + auto locked = tasks_.Lock(); |
| 78 | + locked.Push(std::make_unique<ScheduleTask>( |
| 79 | + this, std::move(task), delay_in_seconds)); |
78 | 80 | uv_async_send(&flush_tasks_); |
79 | 81 | } |
80 | 82 |
|
81 | 83 | void Stop() { |
82 | | - tasks_.Push(std::make_unique<StopTask>(this)); |
| 84 | + auto locked = tasks_.Lock(); |
| 85 | + locked.Push(std::make_unique<StopTask>(this)); |
83 | 86 | uv_async_send(&flush_tasks_); |
84 | 87 | } |
85 | 88 |
|
@@ -100,8 +103,14 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { |
100 | 103 | static void FlushTasks(uv_async_t* flush_tasks) { |
101 | 104 | DelayedTaskScheduler* scheduler = |
102 | 105 | ContainerOf(&DelayedTaskScheduler::loop_, flush_tasks->loop); |
103 | | - while (std::unique_ptr<Task> task = scheduler->tasks_.Pop()) |
| 106 | + |
| 107 | + std::queue<std::unique_ptr<Task>> tasks_to_run = |
| 108 | + scheduler->tasks_.Lock().PopAll(); |
| 109 | + while (!tasks_to_run.empty()) { |
| 110 | + std::unique_ptr<Task> task = std::move(tasks_to_run.front()); |
| 111 | + tasks_to_run.pop(); |
104 | 112 | task->Run(); |
| 113 | + } |
105 | 114 | } |
106 | 115 |
|
107 | 116 | class StopTask : public Task { |
@@ -149,7 +158,8 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler { |
149 | 158 | static void RunTask(uv_timer_t* timer) { |
150 | 159 | DelayedTaskScheduler* scheduler = |
151 | 160 | ContainerOf(&DelayedTaskScheduler::loop_, timer->loop); |
152 | | - scheduler->pending_worker_tasks_->Push(scheduler->TakeTimerTask(timer)); |
| 161 | + scheduler->pending_worker_tasks_->Lock().Push( |
| 162 | + scheduler->TakeTimerTask(timer)); |
153 | 163 | } |
154 | 164 |
|
155 | 165 | std::unique_ptr<Task> TakeTimerTask(uv_timer_t* timer) { |
@@ -203,7 +213,7 @@ WorkerThreadsTaskRunner::WorkerThreadsTaskRunner(int thread_pool_size) { |
203 | 213 | } |
204 | 214 |
|
205 | 215 | void WorkerThreadsTaskRunner::PostTask(std::unique_ptr<Task> task) { |
206 | | - pending_worker_tasks_.Push(std::move(task)); |
| 216 | + pending_worker_tasks_.Lock().Push(std::move(task)); |
207 | 217 | } |
208 | 218 |
|
209 | 219 | void WorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task, |
@@ -212,11 +222,11 @@ void WorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task, |
212 | 222 | } |
213 | 223 |
|
214 | 224 | void WorkerThreadsTaskRunner::BlockingDrain() { |
215 | | - pending_worker_tasks_.BlockingDrain(); |
| 225 | + pending_worker_tasks_.Lock().BlockingDrain(); |
216 | 226 | } |
217 | 227 |
|
218 | 228 | void WorkerThreadsTaskRunner::Shutdown() { |
219 | | - pending_worker_tasks_.Stop(); |
| 229 | + pending_worker_tasks_.Lock().Stop(); |
220 | 230 | delayed_task_scheduler_->Stop(); |
221 | 231 | for (size_t i = 0; i < threads_.size(); i++) { |
222 | 232 | CHECK_EQ(0, uv_thread_join(threads_[i].get())); |
@@ -253,29 +263,27 @@ void PerIsolatePlatformData::PostIdleTaskImpl( |
253 | 263 |
|
254 | 264 | void PerIsolatePlatformData::PostTaskImpl(std::unique_ptr<Task> task, |
255 | 265 | const v8::SourceLocation& location) { |
256 | | - if (flush_tasks_ == nullptr) { |
257 | | - // V8 may post tasks during Isolate disposal. In that case, the only |
258 | | - // sensible path forward is to discard the task. |
259 | | - return; |
260 | | - } |
261 | | - foreground_tasks_.Push(std::move(task)); |
| 266 | + // The task can be posted from any V8 background worker thread, even when |
| 267 | + // the foreground task runner is being cleaned up by Shutdown(). In that |
| 268 | + // case, make sure we wait until the shutdown is completed (which leads |
| 269 | + // to flush_tasks_ == nullptr, and the task will be discarded). |
| 270 | + auto locked = foreground_tasks_.Lock(); |
| 271 | + if (flush_tasks_ == nullptr) return; |
| 272 | + locked.Push(std::move(task)); |
262 | 273 | uv_async_send(flush_tasks_); |
263 | 274 | } |
264 | 275 |
|
265 | 276 | void PerIsolatePlatformData::PostDelayedTaskImpl( |
266 | 277 | std::unique_ptr<Task> task, |
267 | 278 | double delay_in_seconds, |
268 | 279 | const v8::SourceLocation& location) { |
269 | | - if (flush_tasks_ == nullptr) { |
270 | | - // V8 may post tasks during Isolate disposal. In that case, the only |
271 | | - // sensible path forward is to discard the task. |
272 | | - return; |
273 | | - } |
| 280 | + auto locked = foreground_delayed_tasks_.Lock(); |
| 281 | + if (flush_tasks_ == nullptr) return; |
274 | 282 | std::unique_ptr<DelayedTask> delayed(new DelayedTask()); |
275 | 283 | delayed->task = std::move(task); |
276 | 284 | delayed->platform_data = shared_from_this(); |
277 | 285 | delayed->timeout = delay_in_seconds; |
278 | | - foreground_delayed_tasks_.Push(std::move(delayed)); |
| 286 | + locked.Push(std::move(delayed)); |
279 | 287 | uv_async_send(flush_tasks_); |
280 | 288 | } |
281 | 289 |
|
@@ -301,32 +309,30 @@ void PerIsolatePlatformData::AddShutdownCallback(void (*callback)(void*), |
301 | 309 | } |
302 | 310 |
|
303 | 311 | void PerIsolatePlatformData::Shutdown() { |
304 | | - if (flush_tasks_ == nullptr) |
305 | | - return; |
| 312 | + auto foreground_tasks_locked = foreground_tasks_.Lock(); |
| 313 | + auto foreground_delayed_tasks_locked = foreground_delayed_tasks_.Lock(); |
306 | 314 |
|
307 | | - // While there should be no V8 tasks in the queues at this point, it is |
308 | | - // possible that Node.js-internal tasks from e.g. the inspector are still |
309 | | - // lying around. We clear these queues and ignore the return value, |
310 | | - // effectively deleting the tasks instead of running them. |
311 | | - foreground_delayed_tasks_.PopAll(); |
312 | | - foreground_tasks_.PopAll(); |
| 315 | + foreground_delayed_tasks_locked.PopAll(); |
| 316 | + foreground_tasks_locked.PopAll(); |
313 | 317 | scheduled_delayed_tasks_.clear(); |
314 | 318 |
|
315 | | - // Both destroying the scheduled_delayed_tasks_ lists and closing |
316 | | - // flush_tasks_ handle add tasks to the event loop. We keep a count of all |
317 | | - // non-closed handles, and when that reaches zero, we inform any shutdown |
318 | | - // callbacks that the platform is done as far as this Isolate is concerned. |
319 | | - self_reference_ = shared_from_this(); |
320 | | - uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_), |
321 | | - [](uv_handle_t* handle) { |
322 | | - std::unique_ptr<uv_async_t> flush_tasks { |
323 | | - reinterpret_cast<uv_async_t*>(handle) }; |
324 | | - PerIsolatePlatformData* platform_data = |
325 | | - static_cast<PerIsolatePlatformData*>(flush_tasks->data); |
326 | | - platform_data->DecreaseHandleCount(); |
327 | | - platform_data->self_reference_.reset(); |
328 | | - }); |
329 | | - flush_tasks_ = nullptr; |
| 319 | + if (flush_tasks_ != nullptr) { |
| 320 | + // Both destroying the scheduled_delayed_tasks_ lists and closing |
| 321 | + // flush_tasks_ handle add tasks to the event loop. We keep a count of all |
| 322 | + // non-closed handles, and when that reaches zero, we inform any shutdown |
| 323 | + // callbacks that the platform is done as far as this Isolate is concerned. |
| 324 | + self_reference_ = shared_from_this(); |
| 325 | + uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_), |
| 326 | + [](uv_handle_t* handle) { |
| 327 | + std::unique_ptr<uv_async_t> flush_tasks{ |
| 328 | + reinterpret_cast<uv_async_t*>(handle)}; |
| 329 | + PerIsolatePlatformData* platform_data = |
| 330 | + static_cast<PerIsolatePlatformData*>(flush_tasks->data); |
| 331 | + platform_data->DecreaseHandleCount(); |
| 332 | + platform_data->self_reference_.reset(); |
| 333 | + }); |
| 334 | + flush_tasks_ = nullptr; |
| 335 | + } |
330 | 336 | } |
331 | 337 |
|
332 | 338 | void PerIsolatePlatformData::DecreaseHandleCount() { |
@@ -472,39 +478,48 @@ void NodePlatform::DrainTasks(Isolate* isolate) { |
472 | 478 | bool PerIsolatePlatformData::FlushForegroundTasksInternal() { |
473 | 479 | bool did_work = false; |
474 | 480 |
|
475 | | - while (std::unique_ptr<DelayedTask> delayed = |
476 | | - foreground_delayed_tasks_.Pop()) { |
| 481 | + std::queue<std::unique_ptr<DelayedTask>> delayed_tasks_to_schedule = |
| 482 | + foreground_delayed_tasks_.Lock().PopAll(); |
| 483 | + while (!delayed_tasks_to_schedule.empty()) { |
| 484 | + std::unique_ptr<DelayedTask> delayed = |
| 485 | + std::move(delayed_tasks_to_schedule.front()); |
| 486 | + delayed_tasks_to_schedule.pop(); |
| 487 | + |
477 | 488 | did_work = true; |
478 | 489 | uint64_t delay_millis = llround(delayed->timeout * 1000); |
479 | 490 |
|
480 | 491 | delayed->timer.data = static_cast<void*>(delayed.get()); |
481 | 492 | uv_timer_init(loop_, &delayed->timer); |
482 | | - // Timers may not guarantee queue ordering of events with the same delay if |
483 | | - // the delay is non-zero. This should not be a problem in practice. |
| 493 | + // Timers may not guarantee queue ordering of events with the same delay |
| 494 | + // if the delay is non-zero. This should not be a problem in practice. |
484 | 495 | uv_timer_start(&delayed->timer, RunForegroundTask, delay_millis, 0); |
485 | 496 | uv_unref(reinterpret_cast<uv_handle_t*>(&delayed->timer)); |
486 | 497 | uv_handle_count_++; |
487 | 498 |
|
488 | | - scheduled_delayed_tasks_.emplace_back(delayed.release(), |
489 | | - [](DelayedTask* delayed) { |
490 | | - uv_close(reinterpret_cast<uv_handle_t*>(&delayed->timer), |
491 | | - [](uv_handle_t* handle) { |
492 | | - std::unique_ptr<DelayedTask> task { |
493 | | - static_cast<DelayedTask*>(handle->data) }; |
494 | | - task->platform_data->DecreaseHandleCount(); |
495 | | - }); |
496 | | - }); |
| 499 | + scheduled_delayed_tasks_.emplace_back( |
| 500 | + delayed.release(), [](DelayedTask* delayed) { |
| 501 | + uv_close(reinterpret_cast<uv_handle_t*>(&delayed->timer), |
| 502 | + [](uv_handle_t* handle) { |
| 503 | + std::unique_ptr<DelayedTask> task{ |
| 504 | + static_cast<DelayedTask*>(handle->data)}; |
| 505 | + task->platform_data->DecreaseHandleCount(); |
| 506 | + }); |
| 507 | + }); |
| 508 | + } |
| 509 | + |
| 510 | + std::queue<std::unique_ptr<Task>> tasks; |
| 511 | + { |
| 512 | + auto locked = foreground_tasks_.Lock(); |
| 513 | + tasks = locked.PopAll(); |
497 | 514 | } |
498 | | - // Move all foreground tasks into a separate queue and flush that queue. |
499 | | - // This way tasks that are posted while flushing the queue will be run on the |
500 | | - // next call of FlushForegroundTasksInternal. |
501 | | - std::queue<std::unique_ptr<Task>> tasks = foreground_tasks_.PopAll(); |
| 515 | + |
502 | 516 | while (!tasks.empty()) { |
503 | 517 | std::unique_ptr<Task> task = std::move(tasks.front()); |
504 | 518 | tasks.pop(); |
505 | 519 | did_work = true; |
506 | 520 | RunForegroundTask(std::move(task)); |
507 | 521 | } |
| 522 | + |
508 | 523 | return did_work; |
509 | 524 | } |
510 | 525 |
|
@@ -594,66 +609,63 @@ TaskQueue<T>::TaskQueue() |
594 | 609 | outstanding_tasks_(0), stopped_(false), task_queue_() { } |
595 | 610 |
|
596 | 611 | template <class T> |
597 | | -void TaskQueue<T>::Push(std::unique_ptr<T> task) { |
598 | | - Mutex::ScopedLock scoped_lock(lock_); |
599 | | - outstanding_tasks_++; |
600 | | - task_queue_.push(std::move(task)); |
601 | | - tasks_available_.Signal(scoped_lock); |
| 612 | +TaskQueue<T>::Locked::Locked(TaskQueue* queue) |
| 613 | + : queue_(queue), lock_(queue->lock_) {} |
| 614 | + |
| 615 | +template <class T> |
| 616 | +void TaskQueue<T>::Locked::Push(std::unique_ptr<T> task) { |
| 617 | + queue_->outstanding_tasks_++; |
| 618 | + queue_->task_queue_.push(std::move(task)); |
| 619 | + queue_->tasks_available_.Signal(lock_); |
602 | 620 | } |
603 | 621 |
|
604 | 622 | template <class T> |
605 | | -std::unique_ptr<T> TaskQueue<T>::Pop() { |
606 | | - Mutex::ScopedLock scoped_lock(lock_); |
607 | | - if (task_queue_.empty()) { |
| 623 | +std::unique_ptr<T> TaskQueue<T>::Locked::Pop() { |
| 624 | + if (queue_->task_queue_.empty()) { |
608 | 625 | return std::unique_ptr<T>(nullptr); |
609 | 626 | } |
610 | | - std::unique_ptr<T> result = std::move(task_queue_.front()); |
611 | | - task_queue_.pop(); |
| 627 | + std::unique_ptr<T> result = std::move(queue_->task_queue_.front()); |
| 628 | + queue_->task_queue_.pop(); |
612 | 629 | return result; |
613 | 630 | } |
614 | 631 |
|
615 | 632 | template <class T> |
616 | | -std::unique_ptr<T> TaskQueue<T>::BlockingPop() { |
617 | | - Mutex::ScopedLock scoped_lock(lock_); |
618 | | - while (task_queue_.empty() && !stopped_) { |
619 | | - tasks_available_.Wait(scoped_lock); |
| 633 | +std::unique_ptr<T> TaskQueue<T>::Locked::BlockingPop() { |
| 634 | + while (queue_->task_queue_.empty() && !queue_->stopped_) { |
| 635 | + queue_->tasks_available_.Wait(lock_); |
620 | 636 | } |
621 | | - if (stopped_) { |
| 637 | + if (queue_->stopped_) { |
622 | 638 | return std::unique_ptr<T>(nullptr); |
623 | 639 | } |
624 | | - std::unique_ptr<T> result = std::move(task_queue_.front()); |
625 | | - task_queue_.pop(); |
| 640 | + std::unique_ptr<T> result = std::move(queue_->task_queue_.front()); |
| 641 | + queue_->task_queue_.pop(); |
626 | 642 | return result; |
627 | 643 | } |
628 | 644 |
|
629 | 645 | template <class T> |
630 | | -void TaskQueue<T>::NotifyOfCompletion() { |
631 | | - Mutex::ScopedLock scoped_lock(lock_); |
632 | | - if (--outstanding_tasks_ == 0) { |
633 | | - tasks_drained_.Broadcast(scoped_lock); |
| 646 | +void TaskQueue<T>::Locked::NotifyOfCompletion() { |
| 647 | + if (--queue_->outstanding_tasks_ == 0) { |
| 648 | + queue_->tasks_drained_.Broadcast(lock_); |
634 | 649 | } |
635 | 650 | } |
636 | 651 |
|
637 | 652 | template <class T> |
638 | | -void TaskQueue<T>::BlockingDrain() { |
639 | | - Mutex::ScopedLock scoped_lock(lock_); |
640 | | - while (outstanding_tasks_ > 0) { |
641 | | - tasks_drained_.Wait(scoped_lock); |
| 653 | +void TaskQueue<T>::Locked::BlockingDrain() { |
| 654 | + while (queue_->outstanding_tasks_ > 0) { |
| 655 | + queue_->tasks_drained_.Wait(lock_); |
642 | 656 | } |
643 | 657 | } |
644 | 658 |
|
645 | 659 | template <class T> |
646 | | -void TaskQueue<T>::Stop() { |
647 | | - Mutex::ScopedLock scoped_lock(lock_); |
648 | | - stopped_ = true; |
649 | | - tasks_available_.Broadcast(scoped_lock); |
| 660 | +void TaskQueue<T>::Locked::Stop() { |
| 661 | + queue_->stopped_ = true; |
| 662 | + queue_->tasks_available_.Broadcast(lock_); |
650 | 663 | } |
651 | 664 |
|
652 | 665 | template <class T> |
653 | | -std::queue<std::unique_ptr<T>> TaskQueue<T>::PopAll() { |
654 | | - Mutex::ScopedLock scoped_lock(lock_); |
| 666 | +std::queue<std::unique_ptr<T>> TaskQueue<T>::Locked::PopAll() { |
655 | 667 | std::queue<std::unique_ptr<T>> result; |
656 | | - result.swap(task_queue_); |
| 668 | + result.swap(queue_->task_queue_); |
657 | 669 | return result; |
658 | 670 | } |
659 | 671 |
|
|