Commit e77e1730044 for nodejs

commit e77e1730044457a97082ba4ecf02b4b54875fc4a
Author: Nigro Simone <nigro.simone@gmail.com>
Date:   Sat Sep 26 14:59:55 2026 +0200

    benchmark: add a node::MakeCallback benchmark

    The addon calls into JS with node::MakeCallback from a libuv timer, so
    every call opens a top-level callback scope, like an I/O callback does.

    Signed-off-by: Nigro Simone <nigro.simone@gmail.com>
    PR-URL: https://github.com/nodejs/node/pull/66316
    Refs: https://github.com/nodejs/performance/issues/24
    Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
    Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
    Reviewed-By: James M Snell <jasnell@gmail.com>

diff --git a/benchmark/napi/make_callback/.gitignore b/benchmark/napi/make_callback/.gitignore
new file mode 100644
index 00000000000..567609b1234
--- /dev/null
+++ b/benchmark/napi/make_callback/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/benchmark/napi/make_callback/binding.cc b/benchmark/napi/make_callback/binding.cc
new file mode 100644
index 00000000000..85d335170fb
--- /dev/null
+++ b/benchmark/napi/make_callback/binding.cc
@@ -0,0 +1,58 @@
+#include <node.h>
+#include <uv.h>
+#include <v8.h>
+
+using v8::Context;
+using v8::Function;
+using v8::FunctionCallbackInfo;
+using v8::Global;
+using v8::HandleScope;
+using v8::Isolate;
+using v8::Local;
+using v8::Object;
+using v8::Value;
+
+struct State {
+  uv_timer_t timer;
+  Isolate* isolate;
+  int64_t n;
+  Global<Function> fn;
+  Global<Function> done;
+};
+
+static void OnTimer(uv_timer_t* handle) {
+  State* state = static_cast<State*>(handle->data);
+  Isolate* isolate = state->isolate;
+  HandleScope handle_scope(isolate);
+  Local<Function> fn = state->fn.Get(isolate);
+  Local<Context> context = fn->GetCreationContextChecked(isolate);
+  Context::Scope context_scope(context);
+  Local<Object> recv = context->Global();
+  for (int64_t i = 0; i < state->n; i++) {
+    HandleScope inner_scope(isolate);
+    (void)node::MakeCallback(isolate, recv, fn, 0, nullptr, {0, 0});
+  }
+  Local<Function> done = state->done.Get(isolate);
+  (void)node::MakeCallback(isolate, recv, done, 0, nullptr, {0, 0});
+  uv_close(reinterpret_cast<uv_handle_t*>(&state->timer),
+           [](uv_handle_t* h) { delete static_cast<State*>(h->data); });
+}
+
+// run(n, fn, done): calls fn n times from a timer, then calls done.
+static void Run(const FunctionCallbackInfo<Value>& args) {
+  Isolate* isolate = args.GetIsolate();
+  State* state = new State;
+  state->isolate = isolate;
+  state->n = args[0]->IntegerValue(isolate->GetCurrentContext()).FromJust();
+  state->fn.Reset(isolate, args[1].As<Function>());
+  state->done.Reset(isolate, args[2].As<Function>());
+  state->timer.data = state;
+  uv_timer_init(node::GetCurrentEventLoop(isolate), &state->timer);
+  uv_timer_start(&state->timer, OnTimer, 0, 0);
+}
+
+static void Initialize(Local<Object> target, Local<Value> module, void* data) {
+  NODE_SET_METHOD(target, "run", Run);
+}
+
+NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
diff --git a/benchmark/napi/make_callback/binding.gyp b/benchmark/napi/make_callback/binding.gyp
new file mode 100644
index 00000000000..3bfb84493f3
--- /dev/null
+++ b/benchmark/napi/make_callback/binding.gyp
@@ -0,0 +1,8 @@
+{
+  'targets': [
+    {
+      'target_name': 'binding',
+      'sources': [ 'binding.cc' ]
+    }
+  ]
+}
diff --git a/benchmark/napi/make_callback/index.js b/benchmark/napi/make_callback/index.js
new file mode 100644
index 00000000000..8c3dba38343
--- /dev/null
+++ b/benchmark/napi/make_callback/index.js
@@ -0,0 +1,23 @@
+'use strict';
+
+const common = require('../../common.js');
+
+// The addon calls into JS with node::MakeCallback from a libuv timer, so
+// every call opens a top-level callback scope, like an I/O callback does.
+
+let binding;
+try {
+  binding = require(`./build/${common.buildType}/binding`);
+} catch {
+  console.error('napi/make_callback/index.js Binding failed to load');
+  process.exit(0);
+}
+
+const bench = common.createBenchmark(main, {
+  n: [1e6, 1e7],
+});
+
+function main({ n }) {
+  bench.start();
+  binding.run(n, () => {}, () => bench.end(n));
+}