Skip to content

Commit 2e39701

Browse files
authored
Improvements for cancelable operations (#1701)
- Use performance.now() for time measurements - Use the startCancelableOperation function to reset the lastTick variable
1 parent 58e28b7 commit 2e39701

File tree

5 files changed

+23
-23
lines changed

5 files changed

+23
-23
lines changed

packages/langium/src/utils/promise-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ let globalInterruptionPeriod = 10;
3131
* Reset the global interruption period and create a cancellation token source.
3232
*/
3333
export function startCancelableOperation(): AbstractCancellationTokenSource {
34-
lastTick = Date.now();
34+
lastTick = performance.now();
3535
return new CancellationTokenSource();
3636
}
3737

@@ -74,7 +74,7 @@ export async function interruptAndCheck(token: CancellationToken): Promise<void>
7474
// Early exit in case cancellation was disabled by the caller
7575
return;
7676
}
77-
const current = Date.now();
77+
const current = performance.now();
7878
if (current - lastTick >= globalInterruptionPeriod) {
7979
lastTick = current;
8080
await delayNextTick();

packages/langium/src/workspace/workspace-lock.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* terms of the MIT License, which is available in the project root.
55
******************************************************************************/
66

7-
import { CancellationToken, CancellationTokenSource } from '../utils/cancellation.js';
8-
import { Deferred, isOperationCancelled, type MaybePromise } from '../utils/promise-utils.js';
7+
import { type AbstractCancellationTokenSource, CancellationToken, CancellationTokenSource } from '../utils/cancellation.js';
8+
import { Deferred, isOperationCancelled, startCancelableOperation, type MaybePromise } from '../utils/promise-utils.js';
99

1010
/**
1111
* Utility service to execute mutually exclusive actions.
@@ -47,14 +47,14 @@ interface LockEntry {
4747

4848
export class DefaultWorkspaceLock implements WorkspaceLock {
4949

50-
private previousTokenSource = new CancellationTokenSource();
50+
private previousTokenSource: AbstractCancellationTokenSource = new CancellationTokenSource();
5151
private writeQueue: LockEntry[] = [];
5252
private readQueue: LockEntry[] = [];
5353
private done = true;
5454

5555
write(action: (token: CancellationToken) => MaybePromise<void>): Promise<void> {
5656
this.cancelWrite();
57-
const tokenSource = new CancellationTokenSource();
57+
const tokenSource = startCancelableOperation();
5858
this.previousTokenSource = tokenSource;
5959
return this.enqueue(this.writeQueue, action, tokenSource.token);
6060
}

packages/langium/test/parser/worker-thread-async-parser.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { WorkerThreadAsyncParser } from 'langium/node';
99
import { createLangiumGrammarServices } from 'langium/grammar';
1010
import type { Grammar, LangiumCoreServices, ParseResult } from 'langium';
1111
import type { LangiumServices } from 'langium/lsp';
12-
import { EmptyFileSystem, GrammarUtils, CstUtils, GrammarAST, isOperationCancelled } from 'langium';
13-
import { CancellationToken, CancellationTokenSource } from 'vscode-languageserver';
12+
import { EmptyFileSystem, GrammarUtils, CstUtils, GrammarAST, isOperationCancelled, startCancelableOperation } from 'langium';
13+
import { CancellationToken } from 'vscode-languageserver';
1414
import { fail } from 'node:assert';
1515
import { fileURLToPath } from 'node:url';
1616

@@ -46,16 +46,16 @@ describe('WorkerThreadAsyncParser', () => {
4646
// This file should take a few seconds to parse
4747
const file = createLargeFile(100000);
4848
const asyncParser = services.parser.AsyncParser;
49-
const cancellationTokenSource = new CancellationTokenSource();
49+
const cancellationTokenSource = startCancelableOperation();
5050
setTimeout(() => cancellationTokenSource.cancel(), 50);
51-
const start = Date.now();
51+
const start = performance.now();
5252
try {
5353
await asyncParser.parse<Grammar>(file, cancellationTokenSource.token);
5454
fail('Parsing should have been cancelled');
5555
} catch (err) {
5656
expect(isOperationCancelled(err)).toBe(true);
5757
}
58-
const end = Date.now();
58+
const end = performance.now();
5959
// The whole parsing process should have been successfully cancelled within a second
6060
expect(end - start).toBeLessThan(1000);
6161
});
@@ -65,7 +65,7 @@ describe('WorkerThreadAsyncParser', () => {
6565
// This file should take a few seconds to parse
6666
const file = createLargeFile(100000);
6767
const asyncParser = services.parser.AsyncParser;
68-
const cancellationTokenSource = new CancellationTokenSource();
68+
const cancellationTokenSource = startCancelableOperation();
6969
setTimeout(() => cancellationTokenSource.cancel(), 50);
7070
try {
7171
await asyncParser.parse<Grammar>(file, cancellationTokenSource.token);

packages/langium/test/workspace/document-builder.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
******************************************************************************/
66

77
import type { AstNode, DocumentBuilder, FileSystemProvider, LangiumDocument, LangiumDocumentFactory, LangiumDocuments, Module, Reference, ValidationChecks } from 'langium';
8-
import { AstUtils, DocumentState, TextDocument, URI, isOperationCancelled } from 'langium';
8+
import { AstUtils, DocumentState, TextDocument, URI, isOperationCancelled, startCancelableOperation } from 'langium';
99
import { createServicesForGrammar } from 'langium/grammar';
1010
import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
11-
import { CancellationToken, CancellationTokenSource } from 'vscode-languageserver';
11+
import { CancellationToken } from 'vscode-languageserver';
1212
import { fail } from 'assert';
1313
import type { LangiumServices, LangiumSharedServices, TextDocuments } from 'langium/lsp';
1414

@@ -147,7 +147,7 @@ describe('DefaultDocumentBuilder', () => {
147147
documents.addDocument(document2);
148148

149149
const builder = workspace.DocumentBuilder;
150-
const tokenSource1 = new CancellationTokenSource();
150+
const tokenSource1 = startCancelableOperation();
151151
builder.onBuildPhase(DocumentState.IndexedContent, () => {
152152
tokenSource1.cancel();
153153
});
@@ -316,7 +316,7 @@ describe('DefaultDocumentBuilder', () => {
316316
documents.addDocument(document);
317317

318318
const actual: string[] = [];
319-
const cancelTokenSource = new CancellationTokenSource();
319+
const cancelTokenSource = startCancelableOperation();
320320
function wait(state: DocumentState): void {
321321
builder.onBuildPhase(state, async () => {
322322
actual.push('B' + state);
@@ -351,7 +351,7 @@ describe('DefaultDocumentBuilder', () => {
351351
const document = documentFactory.fromString('', URI.parse('file:///test1.txt'));
352352
documents.addDocument(document);
353353

354-
const cancelTokenSource = new CancellationTokenSource();
354+
const cancelTokenSource = startCancelableOperation();
355355
builder.waitUntil(DocumentState.IndexedReferences, cancelTokenSource.token).then(() => {
356356
fail('The test should fail here because the cancellation should reject the promise');
357357
}).catch(err => {
@@ -413,7 +413,7 @@ describe('DefaultDocumentBuilder', () => {
413413
documents.addDocument(document2);
414414

415415
const builder = services.shared.workspace.DocumentBuilder;
416-
const tokenSource = new CancellationTokenSource();
416+
const tokenSource = startCancelableOperation();
417417

418418
const buildPhases = new Set<DocumentState>();
419419

@@ -493,7 +493,7 @@ describe('DefaultDocumentBuilder', () => {
493493
`, URI.parse('file:///test1.txt'));
494494
documents.addDocument(document);
495495

496-
const tokenSource = new CancellationTokenSource();
496+
const tokenSource = startCancelableOperation();
497497
const builder = workspace.DocumentBuilder;
498498
builder.onBuildPhase(DocumentState.ComputedScopes, () => {
499499
tokenSource.cancel();
@@ -636,7 +636,7 @@ describe('DefaultDocumentBuilder', () => {
636636

637637
expect(sortedDocs.slice(0, openDocumentCount).every(isDocumentOpen)).toBe(true);
638638
expect(sortedDocs.slice(openDocumentCount).every(doc => !isDocumentOpen(doc))).toBe(true);
639-
expect(endTime - startTime).toBeLessThan(1000); // Adjust this threshold as needed
639+
expect(endTime - startTime).toBeLessThan(1500); // Adjust this threshold as needed
640640
});
641641

642642
test('Sorting an empty list of documents', async () => {

packages/langium/test/workspace/workspace-lock.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ describe('WorkspaceLock', () => {
5959

6060
test('Write action results can be awaited', async () => {
6161
const mutex = new DefaultWorkspaceLock();
62-
const now = Date.now();
62+
const now = performance.now();
6363
const magicalNumber = await mutex.read(() => new Promise(resolve => setTimeout(() => resolve(42), 10)));
64-
// Confirm that at least 10ms have elapsed
65-
expect(Date.now() - now).toBeGreaterThanOrEqual(10);
64+
// Confirm that at least 5ms have elapsed
65+
expect(performance.now() - now).toBeGreaterThanOrEqual(5);
6666
// Confirm the returned value
6767
expect(magicalNumber).toBe(42);
6868
});

0 commit comments

Comments
 (0)