From 1959292f369ef83a767872de6efd70b18dac22b5 Mon Sep 17 00:00:00 2001 From: thejoecode <1725110+thejoecode@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:08:11 -0500 Subject: [PATCH 1/2] Prevent interruptAndCheck from delaying too often when delay takes too long I ran into an issue where Firefox setTimeout(resolve, 0) was taking the majority of the time between interruptAndCheck calls. performance.now() - current will be greater than globalInterruptionPeriod so it gets called every iteration As a work around I can greatly increase the globalinterruptionPeriod with something like setInterruptionPeriod(150) - but then other browsers that don't have the slower setTimeout callback will have slower cancellation responses. Other fixes I considered: have delayNextTick return performance.now() only for setTimout path (code below) or letting users provide a function that gets called back with current and they can either return current or process.now() export function delayNextTick(current: number): Promise { return new Promise(resolve => { // In case we are running in a non-node environment, `setImmediate` isn't available. // Using `setTimeout` of the browser API accomplishes the same result. if (typeof setImmediate === 'undefined') { // there is no way to guarantee setTimeout(() => resolve(performance.now()), 0); } else { setImmediate(() => resolve(current)); } }); } --- packages/langium/src/utils/promise-utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/langium/src/utils/promise-utils.ts b/packages/langium/src/utils/promise-utils.ts index c7b8aea99..9af1519e2 100644 --- a/packages/langium/src/utils/promise-utils.ts +++ b/packages/langium/src/utils/promise-utils.ts @@ -76,8 +76,11 @@ export async function interruptAndCheck(token: CancellationToken): Promise } const current = performance.now(); if (current - lastTick >= globalInterruptionPeriod) { - lastTick = current; await delayNextTick(); + // prevent calling delayNextTick every iteration of loop + // where delayNextTick takes up the majority or all of the + // globalInterruptionPeriod itself + lastTick = performance.now(); } if (token.isCancellationRequested) { throw OperationCancelled; From 789d4e82a2a3a7203e4dfe7c633bd214ebc0e2d7 Mon Sep 17 00:00:00 2001 From: thejoecode <1725110+thejoecode@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:38:19 -0500 Subject: [PATCH 2/2] Update promise-utils.ts Putting initial set of lastTick to current back from feedback @msujew wrote: If we interrupt to let other code run (i.e. how it's intended to be used, not just waiting on FireFox to do it's thing), the newly called code will then also call interruptAndCheck which immediately results in another next tick delay. We don't want that, so we would need to update the lastTick before and after running the delay. --- packages/langium/src/utils/promise-utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/langium/src/utils/promise-utils.ts b/packages/langium/src/utils/promise-utils.ts index 9af1519e2..4540c4ace 100644 --- a/packages/langium/src/utils/promise-utils.ts +++ b/packages/langium/src/utils/promise-utils.ts @@ -76,6 +76,7 @@ export async function interruptAndCheck(token: CancellationToken): Promise } const current = performance.now(); if (current - lastTick >= globalInterruptionPeriod) { + lastTick = current; await delayNextTick(); // prevent calling delayNextTick every iteration of loop // where delayNextTick takes up the majority or all of the