Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/agentHost/node/diffComputeService.ts
13394 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Worker } from 'worker_threads';
7
import { Disposable } from '../../../base/common/lifecycle.js';
8
import { FileAccess } from '../../../base/common/network.js';
9
import { ILogService } from '../../log/common/log.js';
10
import { DEFAULT_DIFF_TIMEOUT_MS, IDiffComputeService, type IDiffCountResult } from '../common/diffComputeService.js';
11
12
/**
13
* Node.js implementation of {@link IDiffComputeService} that runs
14
* {@link DefaultLinesDiffComputer} in a worker thread to avoid blocking
15
* the main thread.
16
*/
17
export class NodeWorkerDiffComputeService extends Disposable implements IDiffComputeService {
18
19
declare readonly _serviceBrand: undefined;
20
21
private _worker: Worker | undefined;
22
private _workerFailures = 0;
23
private _nextId = 1;
24
private readonly _pending = new Map<number, { resolve: (value: IDiffCountResult) => void; reject: (err: Error) => void }>();
25
26
constructor(
27
@ILogService private readonly _logService: ILogService,
28
) {
29
super();
30
}
31
32
async computeDiffCounts(original: string, modified: string, timeoutMs: number = DEFAULT_DIFF_TIMEOUT_MS): Promise<IDiffCountResult> {
33
const worker = this._ensureWorker();
34
const id = this._nextId++;
35
return new Promise<IDiffCountResult>((resolve, reject) => {
36
this._pending.set(id, { resolve, reject });
37
try {
38
worker.postMessage({ id, fn: 'computeDiffCounts', args: [original, modified, timeoutMs] });
39
} catch (err) {
40
this._pending.delete(id);
41
reject(err instanceof Error ? err : new Error(String(err)));
42
}
43
});
44
}
45
46
private _ensureWorker(): Worker {
47
if (this._workerFailures >= 3) {
48
throw new Error('Diff compute worker failed too many times');
49
}
50
if (!this._worker) {
51
const workerPath = FileAccess.asFileUri('vs/platform/agentHost/node/diffWorkerMain.js').fsPath;
52
const w = new Worker(workerPath, { name: 'Diff compute worker' });
53
w.on('message', (msg: { id: number; res?: IDiffCountResult; err?: { message: string; stack?: string } }) => {
54
const handler = this._pending.get(msg.id);
55
if (!handler) {
56
return;
57
}
58
this._pending.delete(msg.id);
59
if (msg.err) {
60
const error = new Error(msg.err.message);
61
if (msg.err.stack) {
62
error.stack = msg.err.stack;
63
}
64
handler.reject(error);
65
} else {
66
handler.resolve(msg.res!);
67
}
68
});
69
w.on('error', err => {
70
this._logService.error('[DiffComputeService] Worker error', err);
71
for (const [, handler] of this._pending) {
72
handler.reject(err);
73
}
74
this._pending.clear();
75
this._worker = undefined;
76
this._workerFailures++;
77
});
78
this._worker = w;
79
}
80
return this._worker;
81
}
82
83
override dispose(): void {
84
if (this._worker) {
85
this._worker.terminate();
86
this._worker = undefined;
87
}
88
for (const [, handler] of this._pending) {
89
handler.reject(new Error('DiffComputeService disposed'));
90
}
91
this._pending.clear();
92
super.dispose();
93
}
94
}
95
96