Skip to content

Commit 6bf816f

Browse files
ulanMylesBorins
authored andcommitted
src: limit foreground tasks draining loop
Foreground tasks that repost themselves can force the draining loop to run indefinitely long without giving other tasks chance to run. This limits the foreground task draining loop to run only the tasks that were in the tasks queue at the beginning of the loop. PR-URL: #19987Fixes: #19937 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yang Guo <yangguo@chromium.org> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Khaidi Chu <i@2333.moe>
1 parent 99e0b91 commit 6bf816f

5 files changed

Lines changed: 83 additions & 7 deletions

File tree

‎node.gyp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,7 @@
961961
'test/cctest/test_base64.cc',
962962
'test/cctest/test_node_postmortem_metadata.cc',
963963
'test/cctest/test_environment.cc',
964+
'test/cctest/test_platform.cc',
964965
'test/cctest/test_util.cc',
965966
'test/cctest/test_url.cc'
966967
],

‎src/inspector_agent.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ class NodeInspectorClient : public V8InspectorClient {
316316
terminated_ = false;
317317
running_nested_loop_ = true;
318318
while (!terminated_ && channel_->waitForFrontendMessage()) {
319-
platform_->FlushForegroundTasks(env_->isolate());
319+
while (platform_->FlushForegroundTasks(env_->isolate())) {}
320320
}
321321
terminated_ = false;
322322
running_nested_loop_ = false;

‎src/node_platform.cc‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void PerIsolatePlatformData::PostDelayedTask(
9595
}
9696

9797
PerIsolatePlatformData::~PerIsolatePlatformData() {
98-
FlushForegroundTasksInternal();
98+
while (FlushForegroundTasksInternal()) {}
9999
CancelPendingDelayedTasks();
100100

101101
uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_),
@@ -223,7 +223,13 @@ bool PerIsolatePlatformData::FlushForegroundTasksInternal() {
223223
});
224224
});
225225
}
226-
while (std::unique_ptr<Task> task = foreground_tasks_.Pop()) {
226+
// Move all foreground tasks into a separate queue and flush that queue.
227+
// This way tasks that are posted while flushing the queue will be run on the
228+
// next call of FlushForegroundTasksInternal.
229+
std::queue<std::unique_ptr<Task>> tasks = foreground_tasks_.PopAll();
230+
while (!tasks.empty()) {
231+
std::unique_ptr<Task> task = std::move(tasks.front());
232+
tasks.pop();
227233
did_work = true;
228234
RunForegroundTask(std::move(task));
229235
}
@@ -254,8 +260,8 @@ void NodePlatform::CallDelayedOnForegroundThread(Isolate* isolate,
254260
std::unique_ptr<Task>(task), delay_in_seconds);
255261
}
256262

257-
voidNodePlatform::FlushForegroundTasks(v8::Isolate* isolate) {
258-
ForIsolate(isolate)->FlushForegroundTasksInternal();
263+
boolNodePlatform::FlushForegroundTasks(v8::Isolate* isolate) {
264+
returnForIsolate(isolate)->FlushForegroundTasksInternal();
259265
}
260266

261267
voidNodePlatform::CancelPendingDelayedTasks(v8::Isolate* isolate) {
@@ -348,4 +354,12 @@ void TaskQueue<T>::Stop() {
348354
tasks_available_.Broadcast(scoped_lock);
349355
}
350356

357+
template <classT>
358+
std::queue<std::unique_ptr<T>> TaskQueue<T>::PopAll() {
359+
Mutex::ScopedLock scoped_lock(lock_);
360+
std::queue<std::unique_ptr<T>> result;
361+
result.swap(task_queue_);
362+
return result;
363+
}
364+
351365
} // namespace node

‎src/node_platform.h‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class TaskQueue {
2626
voidPush(std::unique_ptr<T> task);
2727
std::unique_ptr<T> Pop();
2828
std::unique_ptr<T> BlockingPop();
29+
std::queue<std::unique_ptr<T>> PopAll();
2930
voidNotifyOfCompletion();
3031
voidBlockingDrain();
3132
voidStop();
@@ -65,7 +66,9 @@ class PerIsolatePlatformData :
6566
voidref();
6667
intunref();
6768

68-
// Returns true iff work was dispatched or executed.
69+
// Returns true if work was dispatched or executed. New tasks that are
70+
// posted during flushing of the queue are postponed until the next
71+
// flushing.
6972
boolFlushForegroundTasksInternal();
7073
voidCancelPendingDelayedTasks();
7174

@@ -130,7 +133,10 @@ class NodePlatform : public MultiIsolatePlatform {
130133
doubleCurrentClockTimeMillis() override;
131134
v8::TracingController* GetTracingController() override;
132135

133-
voidFlushForegroundTasks(v8::Isolate* isolate);
136+
// Returns true if work was dispatched or executed. New tasks that are
137+
// posted during flushing of the queue are postponed until the next
138+
// flushing.
139+
boolFlushForegroundTasks(v8::Isolate* isolate);
134140

135141
voidRegisterIsolate(IsolateData* isolate_data, uv_loop_t* loop) override;
136142
voidUnregisterIsolate(IsolateData* isolate_data) override;

‎test/cctest/test_platform.cc‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include"node_internals.h"
2+
#include"libplatform/libplatform.h"
3+
4+
#include<string>
5+
#include"gtest/gtest.h"
6+
#include"node_test_fixture.h"
7+
8+
// This task increments the given run counter and reposts itself until the
9+
// repost counter reaches zero.
10+
classRepostingTask : publicv8::Task {
11+
public:
12+
explicitRepostingTask(int repost_count,
13+
int* run_count,
14+
v8::Isolate* isolate,
15+
node::NodePlatform* platform)
16+
: repost_count_(repost_count),
17+
run_count_(run_count),
18+
isolate_(isolate),
19+
platform_(platform) {}
20+
21+
// v8::Task implementation
22+
voidRun() final {
23+
++*run_count_;
24+
if (repost_count_ > 0) {
25+
--repost_count_;
26+
platform_->CallOnForegroundThread(isolate_,
27+
newRepostingTask(repost_count_, run_count_, isolate_, platform_));
28+
}
29+
}
30+
31+
private:
32+
int repost_count_;
33+
int* run_count_;
34+
v8::Isolate* isolate_;
35+
node::NodePlatform* platform_;
36+
};
37+
38+
classPlatformTest : publicEnvironmentTestFixture {};
39+
40+
TEST_F(PlatformTest, SkipNewTasksInFlushForegroundTasks) {
41+
v8::Isolate::Scope isolate_scope(isolate_);
42+
const v8::HandleScope handle_scope(isolate_);
43+
const Argv argv;
44+
Env env {handle_scope, argv};
45+
int run_count = 0;
46+
platform->CallOnForegroundThread(
47+
isolate_, newRepostingTask(2, &run_count, isolate_, platform.get()));
48+
EXPECT_TRUE(platform->FlushForegroundTasks(isolate_));
49+
EXPECT_EQ(1, run_count);
50+
EXPECT_TRUE(platform->FlushForegroundTasks(isolate_));
51+
EXPECT_EQ(2, run_count);
52+
EXPECT_TRUE(platform->FlushForegroundTasks(isolate_));
53+
EXPECT_EQ(3, run_count);
54+
EXPECT_FALSE(platform->FlushForegroundTasks(isolate_));
55+
}

0 commit comments

Comments
 (0)