diff --git a/lib/internal/util.js b/lib/internal/util.js index 1ff25dbc7d2a86..0a319739187fab 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -26,7 +26,8 @@ const { getHiddenValue, setHiddenValue, arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex, - decorated_private_symbol: kDecoratedPrivateSymbolIndex + decorated_private_symbol: kDecoratedPrivateSymbolIndex, + sleep: _sleep } = internalBinding('util'); const { isNativeError } = internalBinding('types'); @@ -385,6 +386,17 @@ function once(callback) { }; } +let validateUint32; + +function sleep(msec) { + // Lazy-load to avoid a circular dependency. + if (validateUint32 === undefined) + ({ validateUint32 } = require('internal/validators')); + + validateUint32(msec, 'msec'); + _sleep(msec); +} + module.exports = { assertCrypto, cachedResult, @@ -402,6 +414,7 @@ module.exports = { normalizeEncoding, once, promisify, + sleep, spliceOne, removeColors, diff --git a/src/node_util.cc b/src/node_util.cc index 07a7b69dbd9db4..b9553eaaa6763d 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -169,6 +169,12 @@ static void SetHiddenValue(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(maybe_value.FromJust()); } +static void Sleep(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsUint32()); + uint32_t msec = args[0].As()->Value(); + uv_sleep(msec); +} + void ArrayBufferViewHasBuffer(const FunctionCallbackInfo& args) { CHECK(args[0]->IsArrayBufferView()); args.GetReturnValue().Set(args[0].As()->HasBuffer()); @@ -290,6 +296,7 @@ void Initialize(Local target, env->SetMethodNoSideEffect(target, "getOwnNonIndexProperties", GetOwnNonIndexProperties); env->SetMethodNoSideEffect(target, "getConstructorName", GetConstructorName); + env->SetMethod(target, "sleep", Sleep); env->SetMethod(target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer); Local constants = Object::New(env->isolate()); diff --git a/test/common/README.md b/test/common/README.md index 5f8b6cb3090d9d..11827d9743604a 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -45,11 +45,6 @@ tasks. Takes `whitelist` and concats that with predefined `knownGlobals`. -### busyLoop(time) -* `time` [<number>][] - -Blocks for `time` amount of time. - ### canCreateSymLink() * return [<boolean>][] diff --git a/test/common/index.js b/test/common/index.js index 09b7f5577844cb..1be78720810b34 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -481,12 +481,6 @@ function nodeProcessAborted(exitCode, signal) { } } -function busyLoop(time) { - const startTime = Date.now(); - const stopTime = startTime + time; - while (Date.now() < stopTime) {} -} - function isAlive(pid) { try { process.kill(pid, 'SIGCONT'); @@ -744,7 +738,6 @@ function runWithInvalidFD(func) { module.exports = { allowGlobals, buildType, - busyLoop, canCreateSymLink, childShouldThrowAndAbort, createZeroFilledFile, diff --git a/test/common/index.mjs b/test/common/index.mjs index 54f6dc7f173cdf..a5774fc008a9b3 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -39,7 +39,6 @@ const { skip, ArrayStream, nodeProcessAborted, - busyLoop, isAlive, expectWarning, expectsError, @@ -86,7 +85,6 @@ export { skip, ArrayStream, nodeProcessAborted, - busyLoop, isAlive, expectWarning, expectsError, diff --git a/test/parallel/test-timers-nested.js b/test/parallel/test-timers-nested.js index 0ff228abf50311..b983b002c5f5ad 100644 --- a/test/parallel/test-timers-nested.js +++ b/test/parallel/test-timers-nested.js @@ -1,7 +1,9 @@ +// Flags: --expose-internals 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); +const { sleep } = require('internal/util'); // Make sure we test 0ms timers, since they would had always wanted to run on // the current tick, and greater than 0ms timers, for scenarios where the @@ -23,7 +25,7 @@ scenarios.forEach(function(delay) { // Busy loop for the same timeout used for the nested timer to ensure that // we are in fact expiring the nested timer. - common.busyLoop(delay); + sleep(delay); // The purpose of running this assert in nextTick is to make sure it runs // after A but before the next iteration of the libuv event loop. diff --git a/test/parallel/test-timers-next-tick.js b/test/parallel/test-timers-next-tick.js index 89ebf6c8c1a9d7..12335013660423 100644 --- a/test/parallel/test-timers-next-tick.js +++ b/test/parallel/test-timers-next-tick.js @@ -1,6 +1,8 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); +const { sleep } = require('internal/util'); // This test verifies that the next tick queue runs after each // individual Timeout, as well as each individual Immediate. @@ -16,7 +18,7 @@ const t2 = setTimeout(common.mustNotCall(), 1); const t3 = setTimeout(common.mustNotCall(), 1); setTimeout(common.mustCall(), 1); -common.busyLoop(5); +sleep(5); setImmediate(common.mustCall(() => { process.nextTick(() => { diff --git a/test/parallel/test-util-sleep.js b/test/parallel/test-util-sleep.js new file mode 100644 index 00000000000000..e4cc62b8e07d64 --- /dev/null +++ b/test/parallel/test-util-sleep.js @@ -0,0 +1,19 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const assert = require('assert'); +const { sleep } = require('internal/util'); + +[undefined, null, '', {}, true, false].forEach((value) => { + assert.throws( + () => { sleep(value); }, + /The "msec" argument must be of type number/ + ); +}); + +[-1, 3.14, NaN, 4294967296].forEach((value) => { + assert.throws( + () => { sleep(value); }, + /The value of "msec" is out of range/ + ); +}); diff --git a/test/sequential/test-performance-eventloopdelay.js b/test/sequential/test-performance-eventloopdelay.js index 7b9527b3863905..8e7ee4f0ad9cbf 100644 --- a/test/sequential/test-performance-eventloopdelay.js +++ b/test/sequential/test-performance-eventloopdelay.js @@ -1,4 +1,4 @@ -// Flags: --expose-gc +// Flags: --expose-gc --expose-internals 'use strict'; const common = require('../common'); @@ -6,6 +6,7 @@ const assert = require('assert'); const { monitorEventLoopDelay } = require('perf_hooks'); +const { sleep } = require('internal/util'); { const histogram = monitorEventLoopDelay(); @@ -54,7 +55,7 @@ const { histogram.enable(); let m = 5; function spinAWhile() { - common.busyLoop(1000); + sleep(1000); if (--m > 0) { setTimeout(spinAWhile, common.platformTimeout(500)); } else { diff --git a/test/sequential/test-timers-block-eventloop.js b/test/sequential/test-timers-block-eventloop.js index 811216fcb29e7d..6118695c9235a2 100644 --- a/test/sequential/test-timers-block-eventloop.js +++ b/test/sequential/test-timers-block-eventloop.js @@ -1,7 +1,9 @@ +// Flags: --expose-internals 'use strict'; const common = require('../common'); const assert = require('assert'); +const { sleep } = require('internal/util'); let called = false; const t1 = setInterval(() => { @@ -14,5 +16,5 @@ const t1 = setInterval(() => { }, 10); const t2 = setInterval(() => { - common.busyLoop(20); + sleep(20); }, 10); diff --git a/test/sequential/test-timers-blocking-callback.js b/test/sequential/test-timers-blocking-callback.js index 053bc767b8fa13..a5e0f596a34b93 100644 --- a/test/sequential/test-timers-blocking-callback.js +++ b/test/sequential/test-timers-blocking-callback.js @@ -1,3 +1,4 @@ +// Flags: --expose-internals 'use strict'; /* @@ -25,6 +26,7 @@ const common = require('../common'); const assert = require('assert'); +const { sleep } = require('internal/util'); const TIMEOUT = 100; @@ -65,7 +67,7 @@ function blockingCallback(retry, callback) { return callback(); } else { // Block by busy-looping to trigger the issue - common.busyLoop(TIMEOUT); + sleep(TIMEOUT); timeCallbackScheduled = Date.now(); setTimeout(blockingCallback.bind(null, retry, callback), TIMEOUT); diff --git a/test/sequential/test-timers-set-interval-excludes-callback-duration.js b/test/sequential/test-timers-set-interval-excludes-callback-duration.js index 90eb16b0e259b3..b32c7e7e3c2e9c 100644 --- a/test/sequential/test-timers-set-interval-excludes-callback-duration.js +++ b/test/sequential/test-timers-set-interval-excludes-callback-duration.js @@ -1,13 +1,15 @@ +// Flags: --expose-internals 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); +const { sleep } = require('internal/util'); let cntr = 0; let first; const t = setInterval(() => { cntr++; if (cntr === 1) { - common.busyLoop(100); + sleep(100); // Ensure that the event loop passes before the second interval setImmediate(() => assert.strictEqual(cntr, 1)); first = Date.now();