Skip to content

Commit

Permalink
Handle very long waits (#373)
Browse files Browse the repository at this point in the history
  • Loading branch information
kanongil authored Apr 23, 2022
1 parent 3964633 commit 71250a0
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 5 deletions.
32 changes: 28 additions & 4 deletions lib/wait.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
'use strict';

const internals = {};
const internals = {
maxTimer: 2 ** 31 - 1 // ~25 days
};


module.exports = function (timeout, returnValue, options) {

module.exports = function (timeout, returnValue) {
if (typeof timeout === 'bigint') {
timeout = Number(timeout);
}

if (timeout >= Number.MAX_SAFE_INTEGER) { // Thousands of years
timeout = Infinity;
}

if (typeof timeout !== 'number' && timeout !== undefined) {
throw new TypeError('Timeout must be a number');
throw new TypeError('Timeout must be a number or bigint');
}

return new Promise((resolve) => setTimeout(resolve, timeout, returnValue));
return new Promise((resolve) => {

const _setTimeout = options ? options.setTimeout : setTimeout;

const activate = () => {

const time = Math.min(timeout, internals.maxTimer);
timeout -= time;
_setTimeout(() => (timeout > 0 ? activate() : resolve(returnValue)), time);
};

if (timeout !== Infinity) {
activate();
}
});
};
82 changes: 81 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2341,6 +2341,87 @@ describe('wait()', () => {
expect(timeout.after).to.be.undefined();
});

it('delays for timeout ms as bigint', async () => {

const timeout = {};
setTimeout(() => (timeout.before = true), 10);
const wait = Hoek.wait(10n);
setTimeout(() => (timeout.after = true), 10);

await wait;

expect(timeout.before).to.be.true();
expect(timeout.after).to.be.undefined();
});

it('handles timeouts >= 2^31', async () => {

const flow = [];
let no = 0;

const fakeTimeout = function (cb, time) {

const timer = ++no;

flow.push(`CALL(${timer}): ${time}`);
setImmediate(() => {

flow.push(`PRE(${timer})`);
cb();
flow.push(`POST(${timer})`);
});
};

await Hoek.wait(2 ** 31, null, { setTimeout: fakeTimeout });
flow.push('DONE1');
await Hoek.wait(2 ** 32 + 2 ** 30, null, { setTimeout: fakeTimeout });
flow.push('DONE2');

expect(flow).to.equal([
'CALL(1): 2147483647',
'PRE(1)',
'CALL(2): 1',
'POST(1)',
'PRE(2)',
'POST(2)',
'DONE1',
'CALL(3): 2147483647',
'PRE(3)',
'CALL(4): 2147483647',
'POST(3)',
'PRE(4)',
'CALL(5): 1073741826',
'POST(4)',
'PRE(5)',
'POST(5)',
'DONE2'
]);
});

it('returns never resolving promise when timeout >= Number.MAX_SAFE_INTEGER', async () => {

let calls = 0;
const fakeTimeout = function (cb) {

++calls;
process.nextTick(cb);
};

await Hoek.wait(2 ** 31 - 1, null, { setTimeout: fakeTimeout });
expect(calls).to.equal(1);

const waited = Symbol('waited');

const result = await Promise.race([
Hoek.wait(1, waited),
Hoek.wait(Number.MAX_SAFE_INTEGER, null, { setTimeout: fakeTimeout }),
Hoek.wait(Infinity, null, { setTimeout: fakeTimeout })
]);

expect(result).to.be.equal(waited);
expect(calls).to.equal(1);
});

it('handles a return value', async () => {

const uniqueValue = {};
Expand Down Expand Up @@ -2380,7 +2461,6 @@ describe('wait()', () => {

await expect(() => Hoek.wait({})).to.throw();
await expect(() => Hoek.wait(Symbol('hi'))).to.throw();
await expect(() => Hoek.wait(BigInt(10))).to.throw();
});
});

Expand Down

0 comments on commit 71250a0

Please sign in to comment.