diff --git a/js/timers.ts b/js/timers.ts index 96bfc8c0fb..9e9c312ee9 100644 --- a/js/timers.ts +++ b/js/timers.ts @@ -25,6 +25,9 @@ interface Timer { const EPOCH = Date.now(); const APOCALYPSE = 2 ** 32 - 2; +// Timeout values > TIMEOUT_MAX are set to 1. +const TIMEOUT_MAX = 2 ** 31 - 1; + let globalTimeoutDue: number | null = null; let nextTimerId = 1; @@ -50,6 +53,7 @@ function setGlobalTimeout(due: number | null, now: number) { timeout = due - now; assert(timeout >= 0); } + // Send message to the backend. const builder = flatbuffers.createBuilder(); msg.SetTimeout.startSetTimeout(builder); @@ -181,7 +185,16 @@ function setTimer( // and INT32_MAX. Any other value will cause the timer to fire immediately. // We emulate this behavior. const now = getTime(); + if (delay > TIMEOUT_MAX) { + console.warn( + `${delay} does not fit into` + + " a 32-bit signed integer." + + "\nTimeout duration was set to 1." + ); + delay = 1; + } delay = Math.max(0, delay | 0); + // Create a new, unscheduled timer object. const timer = { id: nextTimerId++, diff --git a/js/timers_test.ts b/js/timers_test.ts index 1ea566c439..fe2ff64df6 100644 --- a/js/timers_test.ts +++ b/js/timers_test.ts @@ -155,3 +155,12 @@ test(async function intervalCancelInvalidSilentFail() { // Should silently fail (no panic) clearInterval(2147483647); }); + +test(async function fireCallbackImmediatelyWhenDelayOverMaxValue() { + let count = 0; + setTimeout(() => { + count++; + }, 2 ** 31); + await waitForMs(1); + assertEqual(count, 1); +}); diff --git a/src/msg.fbs b/src/msg.fbs index 4b303535fa..e13b15daa7 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -202,7 +202,7 @@ table Chdir { } table SetTimeout { - timeout: double; + timeout: int; } table Exit { diff --git a/src/ops.rs b/src/ops.rs index f3c80e1ac4..85a731c6a3 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -338,8 +338,7 @@ fn op_set_timeout( ) -> Box { assert_eq!(data.len(), 0); let inner = base.inner_as_set_timeout().unwrap(); - // FIXME why is timeout a double if it's cast immediately to i64/u64?? - let val = inner.timeout() as i64; + let val = inner.timeout(); let timeout_due = if val >= 0 { Some(Instant::now() + Duration::from_millis(val as u64)) } else {