Skip to content

Commit 0bed081

Browse files
committed
src: fast path empty native immediate drain
Signed-off-by: Gürgün Dayıoğlu <[email protected]>
1 parent fcff458 commit 0bed081

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

src/env.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,15 @@ void Environment::RunAndClearInterrupts() {
14141414
void Environment::RunAndClearNativeImmediates(bool only_refed) {
14151415
TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment),
14161416
"RunAndClearNativeImmediates");
1417+
// Skip the callback scope on the common empty path. These are the only
1418+
// queues drained by this function. If a threadsafe immediate is queued
1419+
// concurrently after this check, uv_async_send() schedules another drain.
1420+
if (native_immediates_.size() == 0 &&
1421+
native_immediates_threadsafe_.size() == 0 &&
1422+
native_immediates_interrupts_.size() == 0) {
1423+
return;
1424+
}
1425+
14171426
HandleScope handle_scope(isolate_);
14181427
// In case the Isolate is no longer accessible just use an empty Local. This
14191428
// is not an issue for InternalCallbackScope as this case is already handled

test/cctest/test_environment.cc

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,69 @@ TEST_F(EnvironmentTest, SetImmediateCleanup) {
401401
EXPECT_EQ(called_unref, 0);
402402
}
403403

404+
TEST_F(EnvironmentTest, RunAndClearNativeImmediatesSkipsEmptyScope) {
405+
const v8::HandleScope handle_scope(isolate_);
406+
const Argv argv;
407+
Env env{handle_scope, argv};
408+
v8::Local<v8::Context> context = env.context();
409+
410+
using IntVec = std::vector<int>;
411+
IntVec callback_calls;
412+
v8::Local<v8::Function> must_call =
413+
v8::Function::New(
414+
context,
415+
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
416+
IntVec* callback_calls =
417+
static_cast<IntVec*>(info.Data().As<v8::External>()->Value(
418+
v8::kExternalPointerTypeTagDefault));
419+
callback_calls->push_back(info[0].As<v8::Int32>()->Value());
420+
},
421+
v8::External::New(isolate_,
422+
static_cast<void*>(&callback_calls),
423+
v8::kExternalPointerTypeTagDefault))
424+
.ToLocalChecked();
425+
context->Global()
426+
->Set(context,
427+
v8::String::NewFromUtf8Literal(isolate_, "mustCall"),
428+
must_call)
429+
.Check();
430+
431+
v8::Local<v8::Function> eval_in_env =
432+
node::LoadEnvironment(*env, "return eval;")
433+
.ToLocalChecked()
434+
.As<v8::Function>();
435+
436+
v8::Local<v8::Value> queue_microtask = v8::String::NewFromUtf8Literal(
437+
isolate_, "Promise.resolve().then(() => mustCall(1));");
438+
eval_in_env->Call(context, v8::Null(isolate_), 1, &queue_microtask)
439+
.ToLocalChecked();
440+
EXPECT_TRUE(callback_calls.empty());
441+
(*env)->RunAndClearNativeImmediates();
442+
EXPECT_TRUE(callback_calls.empty());
443+
444+
context->GetMicrotaskQueue()->PerformCheckpoint(isolate_);
445+
EXPECT_EQ(callback_calls, (IntVec{1}));
446+
callback_calls.clear();
447+
448+
queue_microtask = v8::String::NewFromUtf8Literal(
449+
isolate_, "Promise.resolve().then(() => mustCall(2));");
450+
eval_in_env->Call(context, v8::Null(isolate_), 1, &queue_microtask)
451+
.ToLocalChecked();
452+
EXPECT_TRUE(callback_calls.empty());
453+
454+
bool native_immediate_called = false;
455+
(*env)->SetImmediate(
456+
[&](node::Environment* env_arg) {
457+
EXPECT_EQ(env_arg, *env);
458+
native_immediate_called = true;
459+
},
460+
node::CallbackFlags::kRefed);
461+
462+
(*env)->RunAndClearNativeImmediates();
463+
EXPECT_TRUE(native_immediate_called);
464+
EXPECT_EQ(callback_calls, (IntVec{2}));
465+
}
466+
404467
static char hello[] = "hello";
405468

406469
TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) {

0 commit comments

Comments
 (0)