@@ -40,9 +40,10 @@ static void PlatformWorkerThread(void* data) {
4040 worker_data->platform_workers_ready ->Signal (lock);
4141 }
4242
43- while (std::unique_ptr<Task> task = pending_worker_tasks->BlockingPop ()) {
43+ while (std::unique_ptr<Task> task =
44+ pending_worker_tasks->Lock ().BlockingPop ()) {
4445 task->Run ();
45- pending_worker_tasks->NotifyOfCompletion ();
46+ pending_worker_tasks->Lock (). NotifyOfCompletion ();
4647 }
4748}
4849
@@ -73,13 +74,15 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler {
7374 }
7475
7576 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));
7880 uv_async_send (&flush_tasks_);
7981 }
8082
8183 void Stop () {
82- tasks_.Push (std::make_unique<StopTask>(this ));
84+ auto locked = tasks_.Lock ();
85+ locked.Push (std::make_unique<StopTask>(this ));
8386 uv_async_send (&flush_tasks_);
8487 }
8588
@@ -100,8 +103,14 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler {
100103 static void FlushTasks (uv_async_t * flush_tasks) {
101104 DelayedTaskScheduler* scheduler =
102105 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 ();
104112 task->Run ();
113+ }
105114 }
106115
107116 class StopTask : public Task {
@@ -149,7 +158,8 @@ class WorkerThreadsTaskRunner::DelayedTaskScheduler {
149158 static void RunTask (uv_timer_t * timer) {
150159 DelayedTaskScheduler* scheduler =
151160 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));
153163 }
154164
155165 std::unique_ptr<Task> TakeTimerTask (uv_timer_t * timer) {
@@ -203,7 +213,7 @@ WorkerThreadsTaskRunner::WorkerThreadsTaskRunner(int thread_pool_size) {
203213}
204214
205215void WorkerThreadsTaskRunner::PostTask (std::unique_ptr<Task> task) {
206- pending_worker_tasks_.Push (std::move (task));
216+ pending_worker_tasks_.Lock (). Push (std::move (task));
207217}
208218
209219void WorkerThreadsTaskRunner::PostDelayedTask (std::unique_ptr<Task> task,
@@ -212,11 +222,11 @@ void WorkerThreadsTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,
212222}
213223
214224void WorkerThreadsTaskRunner::BlockingDrain () {
215- pending_worker_tasks_.BlockingDrain ();
225+ pending_worker_tasks_.Lock (). BlockingDrain ();
216226}
217227
218228void WorkerThreadsTaskRunner::Shutdown () {
219- pending_worker_tasks_.Stop ();
229+ pending_worker_tasks_.Lock (). Stop ();
220230 delayed_task_scheduler_->Stop ();
221231 for (size_t i = 0 ; i < threads_.size (); i++) {
222232 CHECK_EQ (0 , uv_thread_join (threads_[i].get ()));
@@ -253,29 +263,27 @@ void PerIsolatePlatformData::PostIdleTaskImpl(
253263
254264void PerIsolatePlatformData::PostTaskImpl (std::unique_ptr<Task> task,
255265 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));
262273 uv_async_send (flush_tasks_);
263274}
264275
265276void PerIsolatePlatformData::PostDelayedTaskImpl (
266277 std::unique_ptr<Task> task,
267278 double delay_in_seconds,
268279 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 ;
274282 std::unique_ptr<DelayedTask> delayed (new DelayedTask ());
275283 delayed->task = std::move (task);
276284 delayed->platform_data = shared_from_this ();
277285 delayed->timeout = delay_in_seconds;
278- foreground_delayed_tasks_ .Push (std::move (delayed));
286+ locked .Push (std::move (delayed));
279287 uv_async_send (flush_tasks_);
280288}
281289
@@ -301,32 +309,30 @@ void PerIsolatePlatformData::AddShutdownCallback(void (*callback)(void*),
301309}
302310
303311void 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 () ;
306314
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 ();
313317 scheduled_delayed_tasks_.clear ();
314318
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+ }
330336}
331337
332338void PerIsolatePlatformData::DecreaseHandleCount () {
@@ -472,39 +478,48 @@ void NodePlatform::DrainTasks(Isolate* isolate) {
472478bool PerIsolatePlatformData::FlushForegroundTasksInternal () {
473479 bool did_work = false ;
474480
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+
477488 did_work = true ;
478489 uint64_t delay_millis = llround (delayed->timeout * 1000 );
479490
480491 delayed->timer .data = static_cast <void *>(delayed.get ());
481492 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.
484495 uv_timer_start (&delayed->timer , RunForegroundTask, delay_millis, 0 );
485496 uv_unref (reinterpret_cast <uv_handle_t *>(&delayed->timer ));
486497 uv_handle_count_++;
487498
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 ();
497514 }
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+
502516 while (!tasks.empty ()) {
503517 std::unique_ptr<Task> task = std::move (tasks.front ());
504518 tasks.pop ();
505519 did_work = true ;
506520 RunForegroundTask (std::move (task));
507521 }
522+
508523 return did_work;
509524}
510525
@@ -594,66 +609,63 @@ TaskQueue<T>::TaskQueue()
594609 outstanding_tasks_(0 ), stopped_(false ), task_queue_() { }
595610
596611template <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_);
602620}
603621
604622template <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 ()) {
608625 return std::unique_ptr<T>(nullptr );
609626 }
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 ();
612629 return result;
613630}
614631
615632template <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_);
620636 }
621- if (stopped_) {
637+ if (queue_-> stopped_ ) {
622638 return std::unique_ptr<T>(nullptr );
623639 }
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 ();
626642 return result;
627643}
628644
629645template <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_);
634649 }
635650}
636651
637652template <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_);
642656 }
643657}
644658
645659template <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_);
650663}
651664
652665template <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() {
655667 std::queue<std::unique_ptr<T>> result;
656- result.swap (task_queue_);
668+ result.swap (queue_-> task_queue_ );
657669 return result;
658670}
659671
0 commit comments