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