Skip to content

Commit 33d5262

Browse files
addaleaxtargos
authored andcommitted
src: prepare platform for upstream V8 changes
V8 platform tasks may schedule other tasks (both background and foreground), and may perform asynchronous operations like resolving Promises. To address that: - Run the task queue drain call inside a callback scope. This makes sure asynchronous operations inside it, like resolving promises, lead to the microtask queue and any subsequent operations not being silently forgotten. - Move the task queue drain call before `EmitBeforeExit()` and only run `EmitBeforeExit()` if there is no new event loop work. - Account for possible new foreground tasks scheduled by background tasks in `DrainBackgroundTasks()`. PR-URL: #15428 Reviewed-By: Franziska Hinkelmann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matthew Loring <[email protected]>
1 parent 5f0686a commit 33d5262

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

src/node.cc

+16-29
Original file line numberDiff line numberDiff line change
@@ -1346,30 +1346,6 @@ void AddPromiseHook(v8::Isolate* isolate, promise_hook_func fn, void* arg) {
13461346
env->AddPromiseHook(fn, arg);
13471347
}
13481348

1349-
class InternalCallbackScope {
1350-
public:
1351-
InternalCallbackScope(Environment* env,
1352-
Local<Object> object,
1353-
const async_context& asyncContext);
1354-
~InternalCallbackScope();
1355-
void Close();
1356-
1357-
inline bool Failed() const { return failed_; }
1358-
inline void MarkAsFailed() { failed_ = true; }
1359-
inline bool IsInnerMakeCallback() const {
1360-
return callback_scope_.in_makecallback();
1361-
}
1362-
1363-
private:
1364-
Environment* env_;
1365-
async_context async_context_;
1366-
v8::Local<v8::Object> object_;
1367-
Environment::AsyncCallbackScope callback_scope_;
1368-
bool failed_ = false;
1369-
bool pushed_ids_ = false;
1370-
bool closed_ = false;
1371-
};
1372-
13731349
CallbackScope::CallbackScope(Isolate* isolate,
13741350
Local<Object> object,
13751351
async_context asyncContext)
@@ -1388,17 +1364,21 @@ CallbackScope::~CallbackScope() {
13881364

13891365
InternalCallbackScope::InternalCallbackScope(Environment* env,
13901366
Local<Object> object,
1391-
const async_context& asyncContext)
1367+
const async_context& asyncContext,
1368+
ResourceExpectation expect)
13921369
: env_(env),
13931370
async_context_(asyncContext),
13941371
object_(object),
13951372
callback_scope_(env) {
1396-
CHECK(!object.IsEmpty());
1373+
if (expect == kRequireResource) {
1374+
CHECK(!object.IsEmpty());
1375+
}
13971376

1377+
HandleScope handle_scope(env->isolate());
13981378
// If you hit this assertion, you forgot to enter the v8::Context first.
13991379
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
14001380

1401-
if (env->using_domains()) {
1381+
if (env->using_domains() && !object_.IsEmpty()) {
14021382
failed_ = DomainEnter(env, object_);
14031383
if (failed_)
14041384
return;
@@ -1422,6 +1402,7 @@ InternalCallbackScope::~InternalCallbackScope() {
14221402
void InternalCallbackScope::Close() {
14231403
if (closed_) return;
14241404
closed_ = true;
1405+
HandleScope handle_scope(env_->isolate());
14251406

14261407
if (pushed_ids_)
14271408
env_->async_hooks()->pop_async_id(async_context_.async_id);
@@ -1432,7 +1413,7 @@ void InternalCallbackScope::Close() {
14321413
AsyncWrap::EmitAfter(env_, async_context_.async_id);
14331414
}
14341415

1435-
if (env_->using_domains()) {
1416+
if (env_->using_domains() && !object_.IsEmpty()) {
14361417
failed_ = DomainExit(env_, object_);
14371418
if (failed_) return;
14381419
}
@@ -1473,6 +1454,7 @@ MaybeLocal<Value> InternalMakeCallback(Environment* env,
14731454
int argc,
14741455
Local<Value> argv[],
14751456
async_context asyncContext) {
1457+
CHECK(!recv.IsEmpty());
14761458
InternalCallbackScope scope(env, recv, asyncContext);
14771459
if (scope.Failed()) {
14781460
return Undefined(env->isolate());
@@ -4707,9 +4689,14 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
47074689
do {
47084690
uv_run(env.event_loop(), UV_RUN_DEFAULT);
47094691

4692+
v8_platform.DrainVMTasks();
4693+
4694+
more = uv_loop_alive(env.event_loop());
4695+
if (more)
4696+
continue;
4697+
47104698
EmitBeforeExit(&env);
47114699

4712-
v8_platform.DrainVMTasks();
47134700
// Emit `beforeExit` if the loop became alive either after emitting
47144701
// event, or after running some callbacks.
47154702
more = uv_loop_alive(env.event_loop());

src/node_internals.h

+28
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,36 @@ v8::MaybeLocal<v8::Value> InternalMakeCallback(
274274
v8::Local<v8::Value> argv[],
275275
async_context asyncContext);
276276

277+
class InternalCallbackScope {
278+
public:
279+
// Tell the constructor whether its `object` parameter may be empty or not.
280+
enum ResourceExpectation { kRequireResource, kAllowEmptyResource };
281+
InternalCallbackScope(Environment* env,
282+
v8::Local<v8::Object> object,
283+
const async_context& asyncContext,
284+
ResourceExpectation expect = kRequireResource);
285+
~InternalCallbackScope();
286+
void Close();
287+
288+
inline bool Failed() const { return failed_; }
289+
inline void MarkAsFailed() { failed_ = true; }
290+
inline bool IsInnerMakeCallback() const {
291+
return callback_scope_.in_makecallback();
292+
}
293+
294+
private:
295+
Environment* env_;
296+
async_context async_context_;
297+
v8::Local<v8::Object> object_;
298+
Environment::AsyncCallbackScope callback_scope_;
299+
bool failed_ = false;
300+
bool pushed_ids_ = false;
301+
bool closed_ = false;
302+
};
303+
277304
} // namespace node
278305

306+
279307
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
280308

281309
#endif // SRC_NODE_INTERNALS_H_

src/node_platform.cc

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
#include "node_platform.h"
2+
#include "node_internals.h"
23

34
#include "util.h"
45

56
namespace node {
67

8+
using v8::HandleScope;
79
using v8::Isolate;
10+
using v8::Local;
11+
using v8::Object;
812
using v8::Platform;
913
using v8::Task;
1014
using v8::TracingController;
@@ -63,22 +67,33 @@ size_t NodePlatform::NumberOfAvailableBackgroundThreads() {
6367
return threads_.size();
6468
}
6569

66-
static void RunForegroundTask(uv_timer_t* handle) {
67-
Task* task = static_cast<Task*>(handle->data);
70+
static void RunForegroundTask(Task* task) {
71+
Isolate* isolate = Isolate::GetCurrent();
72+
HandleScope scope(isolate);
73+
Environment* env = Environment::GetCurrent(isolate);
74+
InternalCallbackScope cb_scope(env, Local<Object>(), { 0, 0 },
75+
InternalCallbackScope::kAllowEmptyResource);
6876
task->Run();
6977
delete task;
78+
}
79+
80+
static void RunForegroundTask(uv_timer_t* handle) {
81+
Task* task = static_cast<Task*>(handle->data);
82+
RunForegroundTask(task);
7083
uv_close(reinterpret_cast<uv_handle_t*>(handle), [](uv_handle_t* handle) {
7184
delete reinterpret_cast<uv_timer_t*>(handle);
7285
});
7386
}
7487

7588
void NodePlatform::DrainBackgroundTasks() {
76-
FlushForegroundTasksInternal();
77-
background_tasks_.BlockingDrain();
89+
while (FlushForegroundTasksInternal())
90+
background_tasks_.BlockingDrain();
7891
}
7992

80-
void NodePlatform::FlushForegroundTasksInternal() {
93+
bool NodePlatform::FlushForegroundTasksInternal() {
94+
bool did_work = false;
8195
while (auto delayed = foreground_delayed_tasks_.Pop()) {
96+
did_work = true;
8297
uint64_t delay_millis =
8398
static_cast<uint64_t>(delayed->second + 0.5) * 1000;
8499
uv_timer_t* handle = new uv_timer_t();
@@ -91,9 +106,10 @@ void NodePlatform::FlushForegroundTasksInternal() {
91106
delete delayed;
92107
}
93108
while (Task* task = foreground_tasks_.Pop()) {
94-
task->Run();
95-
delete task;
109+
did_work = true;
110+
RunForegroundTask(task);
96111
}
112+
return did_work;
97113
}
98114

99115
void NodePlatform::CallOnBackgroundThread(Task* task,

src/node_platform.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class NodePlatform : public v8::Platform {
3939
virtual ~NodePlatform() {}
4040

4141
void DrainBackgroundTasks();
42-
void FlushForegroundTasksInternal();
42+
// Returns true iff work was dispatched or executed.
43+
bool FlushForegroundTasksInternal();
4344
void Shutdown();
4445

4546
// v8::Platform implementation.

0 commit comments

Comments
 (0)