From 73eff6f5ffdfc75e29cc500582547f61cd3ca012 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Tue, 21 Jan 2025 10:41:57 -0500 Subject: [PATCH] use internal setImmediate/clearImmediate fns for node:timers --- src/node/internal/internal_timers.ts | 5 +-- src/node/internal/internal_timers_promises.ts | 4 +-- src/node/internal/timers.d.ts | 7 ++++ src/workerd/api/node/BUILD.bazel | 2 ++ src/workerd/api/node/node.h | 6 ++-- .../api/node/tests/timers-nodejs-test.js | 2 +- .../api/node/tests/timers-nodejs-test.wd-test | 2 +- src/workerd/api/node/timers.c++ | 27 +++++++++++++++ src/workerd/api/node/timers.h | 34 +++++++++++++++++++ 9 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 src/node/internal/timers.d.ts create mode 100644 src/workerd/api/node/timers.c++ create mode 100644 src/workerd/api/node/timers.h diff --git a/src/node/internal/internal_timers.ts b/src/node/internal/internal_timers.ts index 6ae00fab6fc..79451076977 100644 --- a/src/node/internal/internal_timers.ts +++ b/src/node/internal/internal_timers.ts @@ -24,6 +24,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. import { validateFunction } from 'node-internal:validators'; +import { default as timersUtil } from 'node-internal:timers'; let clearTimeoutImpl: (obj: Timeout) => void; @@ -134,9 +135,9 @@ export function clearTimeout(timer: unknown): void { } } -export const setImmediate = globalThis.setImmediate.bind(globalThis); +export const setImmediate = timersUtil.setImmediate.bind(timersUtil); -export const clearImmediate = globalThis.clearImmediate.bind(globalThis); +export const clearImmediate = timersUtil.clearImmediate.bind(timersUtil); export function setInterval( callback: (...args: unknown[]) => void, diff --git a/src/node/internal/internal_timers_promises.ts b/src/node/internal/internal_timers_promises.ts index 06c3b1d6171..69ebbb1960a 100644 --- a/src/node/internal/internal_timers_promises.ts +++ b/src/node/internal/internal_timers_promises.ts @@ -102,13 +102,13 @@ export async function setImmediate( const { promise, resolve, reject } = Promise.withResolvers(); - const timer = globalThis.setImmediate(() => { + const timer = timers.setImmediate(() => { resolve(value as T); }); if (signal) { function onCancel(): void { - globalThis.clearImmediate(timer); + timers.clearImmediate(timer); signal?.removeEventListener('abort', onCancel); reject(new AbortError(undefined, { cause: signal?.reason })); } diff --git a/src/node/internal/timers.d.ts b/src/node/internal/timers.d.ts new file mode 100644 index 00000000000..c657eb76d9b --- /dev/null +++ b/src/node/internal/timers.d.ts @@ -0,0 +1,7 @@ +import type { + setImmediate as setImmediateImpl, + clearImmediate as clearImmediateImpl, +} from 'node:timers'; + +export const setImmediate: typeof setImmediateImpl; +export const clearImmediate: typeof clearImmediateImpl; diff --git a/src/workerd/api/node/BUILD.bazel b/src/workerd/api/node/BUILD.bazel index a2708f4ec27..c1349c203ad 100644 --- a/src/workerd/api/node/BUILD.bazel +++ b/src/workerd/api/node/BUILD.bazel @@ -10,6 +10,7 @@ wd_cc_library( "crypto-keys.c++", "diagnostics-channel.c++", "dns.c++", + "timers.c++", "zlib-util.c++", ], hdrs = [ @@ -17,6 +18,7 @@ wd_cc_library( "diagnostics-channel.h", "dns.h", "node.h", + "timers.h", "zlib-util.h", ], implementation_deps = [ diff --git a/src/workerd/api/node/node.h b/src/workerd/api/node/node.h index 9be37c467bf..66500b8341e 100644 --- a/src/workerd/api/node/node.h +++ b/src/workerd/api/node/node.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,8 @@ class CompatibilityFlags: public jsg::Object { V(DiagnosticsChannelModule, "node-internal:diagnostics_channel") \ V(ZlibUtil, "node-internal:zlib") \ V(UrlUtil, "node-internal:url") \ - V(DnsUtil, "node-internal:dns") + V(DnsUtil, "node-internal:dns") \ + V(TimersUtil, "node-internal:timers") // Add to the NODEJS_MODULES_EXPERIMENTAL list any currently in-development // node.js compat C++ modules that should be guarded by the experimental compat @@ -144,4 +146,4 @@ kj::Own getExternalNodeJsCompatModuleBundle(auto fea api::node::CompatibilityFlags, EW_NODE_BUFFER_ISOLATE_TYPES, EW_NODE_CRYPTO_ISOLATE_TYPES, \ EW_NODE_DIAGNOSTICCHANNEL_ISOLATE_TYPES, EW_NODE_ASYNCHOOKS_ISOLATE_TYPES, \ EW_NODE_UTIL_ISOLATE_TYPES, EW_NODE_ZLIB_ISOLATE_TYPES, EW_NODE_URL_ISOLATE_TYPES, \ - EW_NODE_MODULE_ISOLATE_TYPES, EW_NODE_DNS_ISOLATE_TYPES\ + EW_NODE_MODULE_ISOLATE_TYPES, EW_NODE_DNS_ISOLATE_TYPES, EW_NODE_TIMERS_ISOLATE_TYPES\ diff --git a/src/workerd/api/node/tests/timers-nodejs-test.js b/src/workerd/api/node/tests/timers-nodejs-test.js index 8db12f268d1..fc4e063a7fb 100644 --- a/src/workerd/api/node/tests/timers-nodejs-test.js +++ b/src/workerd/api/node/tests/timers-nodejs-test.js @@ -53,7 +53,7 @@ export const testSetImmediate = { { const { promise, resolve } = Promise.withResolvers(); - globalThis.setImmediate( + timers.setImmediate( (...args) => { deepStrictEqual(args, [1, 2, 3]); resolve(); diff --git a/src/workerd/api/node/tests/timers-nodejs-test.wd-test b/src/workerd/api/node/tests/timers-nodejs-test.wd-test index f69236a5326..6da5238a7f9 100644 --- a/src/workerd/api/node/tests/timers-nodejs-test.wd-test +++ b/src/workerd/api/node/tests/timers-nodejs-test.wd-test @@ -8,7 +8,7 @@ const unitTests :Workerd.Config = ( (name = "worker", esModule = embed "timers-nodejs-test.js") ], compatibilityDate = "2025-01-09", - compatibilityFlags = ["nodejs_compat"], + compatibilityFlags = ["nodejs_compat", "no_nodejs_compat_v2"], ) ), ], diff --git a/src/workerd/api/node/timers.c++ b/src/workerd/api/node/timers.c++ new file mode 100644 index 00000000000..249dee41671 --- /dev/null +++ b/src/workerd/api/node/timers.c++ @@ -0,0 +1,27 @@ +#include "timers.h" + +#include +#include + +namespace workerd::api::node { + +// The setImmediate/clearImmediate methods are only exposed on globalThis if the +// node_compat_v2 flag is set. However, we want them exposed via `node:timers` +// generally when just the original node_compat is enabled. Therefore, we provide +// this alternative route to the implementations on ServiceWorkerGlobalScope. +jsg::Ref TimersUtil::setImmediate(jsg::Lock& js, + jsg::Function)> function, + jsg::Arguments args) { + auto context = js.v8Context(); + auto& global = + jsg::extractInternalPointer(context, context->Global()); + return global.setImmediate(js, kj::mv(function), kj::mv(args)); +} + +void TimersUtil::clearImmediate(jsg::Lock& js, kj::Maybe> maybeImmediate) { + auto context = js.v8Context(); + auto& global = + jsg::extractInternalPointer(context, context->Global()); + global.clearImmediate(kj::mv(maybeImmediate)); +} +} // namespace workerd::api::node diff --git a/src/workerd/api/node/timers.h b/src/workerd/api/node/timers.h new file mode 100644 index 00000000000..2a28d31ed88 --- /dev/null +++ b/src/workerd/api/node/timers.h @@ -0,0 +1,34 @@ +// Copyright (c) 2017-2022 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +#pragma once + +#include + +#include + +namespace workerd::api { + +class Immediate; + +namespace node { +class TimersUtil final: public jsg::Object { + public: + TimersUtil() = default; + TimersUtil(jsg::Lock&, const jsg::Url&) {} + + jsg::Ref setImmediate(jsg::Lock& js, + jsg::Function)> function, + jsg::Arguments args); + void clearImmediate(jsg::Lock& js, kj::Maybe> immediate); + + JSG_RESOURCE_TYPE(TimersUtil) { + JSG_METHOD(setImmediate); + JSG_METHOD(clearImmediate); + } +}; + +#define EW_NODE_TIMERS_ISOLATE_TYPES api::node::TimersUtil +} // namespace node + +} // namespace workerd::api