watcher - allow to use latest version of @parcel/watcher behind experimental setting (#228200)

We are pulling this in from `@bpasero/watcher` as a temporary solutionto:
- fix a deadlock issue in upstream
- allow to switch back and forth between the old and the new version
This commit is contained in:
Benjamin Pasero 2024-09-11 13:21:52 +02:00 committed by GitHub
parent fd5ee87cf6
commit 819cf1cd22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 137 additions and 12 deletions

View file

@ -693,6 +693,7 @@
"when": "hasNode",
"allow": [
"@parcel/watcher",
"@bpasero/watcher",
"@vscode/sqlite3",
"@vscode/vscode-languagedetection",
"@vscode/ripgrep",

View file

@ -110,6 +110,12 @@ node-pty/third_party/**
@parcel/watcher/src/**
!@parcel/watcher/build/Release/*.node
@bpasero/watcher/binding.gyp
@bpasero/watcher/build/**
@bpasero/watcher/prebuilds/**
@bpasero/watcher/src/**
!@bpasero/watcher/build/Release/*.node
vsda/build/**
vsda/ci/**
vsda/src/**

View file

@ -83,7 +83,8 @@ function nodeModules(destinationExe, destinationPdb, platform) {
// We don't build the prebuilt node files so we don't scan them
'!**/prebuilds/**/*.node',
// These are 3rd party modules that we should ignore
'!**/@parcel/watcher/**/*']))
'!**/@parcel/watcher/**/*',
'!**/@bpasero/watcher/**/*']))
.pipe(gulp.dest(destinationExe));
};

39
package-lock.json generated
View file

@ -10,6 +10,7 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@bpasero/watcher": "https://github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
@ -940,6 +941,44 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
"node_modules/@bpasero/watcher": {
"version": "2.4.2-alpha.0",
"resolved": "git+ssh://git@github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"integrity": "sha512-8AWyO22MDRxp6zAJxDWXGFCHtBoioaku+x/IQrDT3VADhBRAeCVp/47qCVrHFyU0ixo0m8KGDBN6MtxqsFFU2g==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@bpasero/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"license": "Apache-2.0",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@bpasero/watcher/node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",

View file

@ -75,6 +75,7 @@
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@bpasero/watcher": "https://github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.4",

View file

@ -8,6 +8,7 @@
"name": "vscode-reh",
"version": "0.0.0",
"dependencies": {
"@bpasero/watcher": "https://github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
@ -44,6 +45,44 @@
"yazl": "^2.4.3"
}
},
"node_modules/@bpasero/watcher": {
"version": "2.4.2-alpha.0",
"resolved": "git+ssh://git@github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"integrity": "sha512-8AWyO22MDRxp6zAJxDWXGFCHtBoioaku+x/IQrDT3VADhBRAeCVp/47qCVrHFyU0ixo0m8KGDBN6MtxqsFFU2g==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@bpasero/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"license": "Apache-2.0",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@bpasero/watcher/node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/@microsoft/1ds-core-js": {
"version": "3.2.13",
"resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz",

View file

@ -6,6 +6,7 @@
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@bpasero/watcher": "https://github.com/bpasero/watcher.git#5d29cc732a03c91ecc2c861940a240b01e765c65",
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/proxy-agent": "^0.23.0",

View file

@ -91,6 +91,11 @@ flakySuite('Native Modules (all platforms)', () => {
assert.ok(typeof parcelWatcher.subscribe === 'function', testErrorMessage('@parcel/watcher'));
});
test('@bpasero/watcher', async () => {
const parcelWatcher2 = await import('@bpasero/watcher');
assert.ok(typeof parcelWatcher2.subscribe === 'function', testErrorMessage('@bpasero/watcher'));
});
test('@vscode/deviceid', async () => {
const deviceIdPackage = await import('@vscode/deviceid');
assert.ok(typeof deviceIdPackage.getDeviceId === 'function', testErrorMessage('@vscode/deviceid'));

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as parcelWatcher from '@parcel/watcher';
import * as parcelWatcher2 from '@bpasero/watcher';
import { existsSync, statSync, unlinkSync } from 'fs';
import { tmpdir, homedir } from 'os';
import { URI } from '../../../../../base/common/uri.js';
@ -24,6 +25,9 @@ import { FileChangeType, IFileChange } from '../../../common/files.js';
import { coalesceEvents, IRecursiveWatchRequest, parseWatcherPatterns, IRecursiveWatcherWithSubscribe, isFiltered, IWatcherErrorEvent } from '../../../common/watcher.js';
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
const useParcelWatcher2 = process.env.VSCODE_USE_WATCHER2 === 'true';
const parcelWatcherLib = useParcelWatcher2 ? parcelWatcher2 : parcelWatcher;
export class ParcelWatcherInstance extends Disposable {
private readonly _onDidStop = this._register(new Emitter<{ joinRestart?: Promise<void> }>());
@ -302,7 +306,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
// We already ran before, check for events since
if (counter > 1) {
const parcelEvents = await parcelWatcher.getEventsSince(realPath, snapshotFile, { ignore: this.addPredefinedExcludes(request.excludes), backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
const parcelEvents = await parcelWatcherLib.getEventsSince(realPath, snapshotFile, { ignore: this.addPredefinedExcludes(request.excludes), backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
if (cts.token.isCancellationRequested) {
return;
@ -313,7 +317,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
}
// Store a snapshot of files to the snapshot file
await parcelWatcher.writeSnapshot(realPath, snapshotFile, { ignore: this.addPredefinedExcludes(request.excludes), backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
await parcelWatcherLib.writeSnapshot(realPath, snapshotFile, { ignore: this.addPredefinedExcludes(request.excludes), backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
// Signal we are ready now when the first snapshot was written
if (counter === 1) {
@ -358,7 +362,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
const { realPath, realPathDiffers, realPathLength } = this.normalizePath(request);
try {
const parcelWatcherInstance = await parcelWatcher.subscribe(realPath, (error, parcelEvents) => {
const parcelWatcherInstance = await parcelWatcherLib.subscribe(realPath, (error, parcelEvents) => {
if (watcher.token.isCancellationRequested) {
return; // return early when disposed
}
@ -858,7 +862,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
}
private toMessage(message: string, request?: IRecursiveWatchRequest): string {
return request ? `[File Watcher (parcel)] ${message} (path: ${request.path})` : `[File Watcher (parcel)] ${message}`;
return request ? `[File Watcher (${useParcelWatcher2 ? 'parcel-next' : 'parcel-classic'})] ${message} (path: ${request.path})` : `[File Watcher (${useParcelWatcher2 ? 'parcel-next' : 'parcel-classic'})] ${message}`;
}
protected get recursiveWatcher() { return this; }

View file

@ -7,6 +7,8 @@ import { IUniversalWatchRequest, requestFilterToString } from '../../common/watc
import { INodeJSWatcherInstance, NodeJSWatcher } from './nodejs/nodejsWatcher.js';
import { ParcelWatcher, ParcelWatcherInstance } from './parcel/parcelWatcher.js';
const useParcelWatcher2 = process.env.VSCODE_USE_WATCHER2 === 'true';
export function computeStats(
requests: IUniversalWatchRequest[],
recursiveWatcher: ParcelWatcher,
@ -59,7 +61,7 @@ export function computeStats(
fillNonRecursiveWatcherStats(nonRecursiveWatcheLines, nonRecursiveWatcher);
lines.push(...alignTextColumns(nonRecursiveWatcheLines));
return `\n\n[File Watcher] request stats:\n\n${lines.join('\n')}\n\n`;
return useParcelWatcher2 ? `\n\n[File Watcher NEXT] request stats:\n\n${lines.join('\n')}\n\n` : `\n\n[File Watcher CLASSIC] request stats:\n\n${lines.join('\n')}\n\n`;
}
function alignTextColumns(lines: string[]) {

View file

@ -8,12 +8,13 @@ import { createDecorator } from '../../instantiation/common/instantiation.js';
import { ILogService } from '../../log/common/log.js';
import { IUtilityProcessWorkerCreateConfiguration, IOnDidTerminateUtilityrocessWorkerProcess, IUtilityProcessWorkerConfiguration, IUtilityProcessWorkerProcessExit, IUtilityProcessWorkerService } from '../common/utilityProcessWorkerService.js';
import { IWindowsMainService } from '../../windows/electron-main/windows.js';
import { WindowUtilityProcess } from './utilityProcess.js';
import { IWindowUtilityProcessConfiguration, WindowUtilityProcess } from './utilityProcess.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { hash } from '../../../base/common/hash.js';
import { Event, Emitter } from '../../../base/common/event.js';
import { DeferredPromise } from '../../../base/common/async.js';
import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMainService.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
export const IUtilityProcessWorkerMainService = createDecorator<IUtilityProcessWorkerMainService>('utilityProcessWorker');
@ -32,7 +33,8 @@ export class UtilityProcessWorkerMainService extends Disposable implements IUtil
@ILogService private readonly logService: ILogService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super();
}
@ -50,7 +52,7 @@ export class UtilityProcessWorkerMainService extends Disposable implements IUtil
}
// Create new worker
const worker = new UtilityProcessWorker(this.logService, this.windowsMainService, this.telemetryService, this.lifecycleMainService, configuration);
const worker = new UtilityProcessWorker(this.logService, this.windowsMainService, this.telemetryService, this.lifecycleMainService, this.configurationService, configuration);
if (!worker.spawn()) {
return { reason: { code: 1, signal: 'EINVALID' } };
}
@ -106,6 +108,7 @@ class UtilityProcessWorker extends Disposable {
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
private readonly configuration: IUtilityProcessWorkerCreateConfiguration
) {
super();
@ -122,7 +125,7 @@ class UtilityProcessWorker extends Disposable {
const window = this.windowsMainService.getWindowById(this.configuration.reply.windowId);
const windowPid = window?.win?.webContents.getOSProcessId();
return this.utilityProcess.start({
let configuration: IWindowUtilityProcessConfiguration = {
type: this.configuration.process.type,
entryPoint: this.configuration.process.moduleId,
parentLifecycleBound: windowPid,
@ -131,7 +134,18 @@ class UtilityProcessWorker extends Disposable {
responseWindowId: this.configuration.reply.windowId,
responseChannel: this.configuration.reply.channel,
responseNonce: this.configuration.reply.nonce
});
};
if (this.configuration.process.type === 'fileWatcher' && this.configurationService.getValue<boolean>('files.experimentalWatcherNext') === true) {
configuration = {
...configuration,
env: {
VSCODE_USE_WATCHER2: 'true'
}
};
}
return this.utilityProcess.start(configuration);
}
kill() {

View file

@ -308,6 +308,12 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('watcherInclude', "Configure extra paths to watch for changes inside the workspace. By default, all workspace folders will be watched recursively, except for folders that are symbolic links. You can explicitly add absolute or relative paths to support watching folders that are symbolic links. Relative paths will be resolved to an absolute path using the currently opened workspace."),
'scope': ConfigurationScope.RESOURCE
},
'files.experimentalWatcherNext': { // TODO@bpasero decide on default and experiment enlisting
'type': 'boolean',
'default': false,
'markdownDescription': nls.localize('experimentalWatcherNext', "Enables a newer, experimental version of the file watcher."),
scope: ConfigurationScope.APPLICATION
},
'files.hotExit': hotExitConfiguration,
'files.defaultLanguage': {
'type': 'string',

View file

@ -30,6 +30,7 @@ interface IConfiguration extends IWindowsConfiguration {
workbench?: { enableExperiments?: boolean };
_extensionsGallery?: { enablePPE?: boolean };
accessibility?: { verbosity?: { debug?: boolean } };
files?: { experimentalWatcherNext?: boolean };
}
export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution {
@ -46,7 +47,8 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
'workbench.enableExperiments',
'_extensionsGallery.enablePPE',
'security.restrictUNCAccess',
'accessibility.verbosity.debug'
'accessibility.verbosity.debug',
'files.experimentalWatcherNext'
];
private readonly titleBarStyle = new ChangeObserver<TitlebarStyle>('string');
@ -61,6 +63,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
private readonly enablePPEExtensionsGallery = new ChangeObserver('boolean');
private readonly restrictUNCAccess = new ChangeObserver('boolean');
private readonly accessibilityVerbosityDebug = new ChangeObserver('boolean');
private readonly filesExperimentalWatcherNext = new ChangeObserver('boolean');
constructor(
@IHostService private readonly hostService: IHostService,
@ -123,6 +126,9 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
// Debug accessibility verbosity
processChanged(this.accessibilityVerbosityDebug.handleChange(config?.accessibility?.verbosity?.debug));
// File watcher next
processChanged(this.filesExperimentalWatcherNext.handleChange(config?.files?.experimentalWatcherNext));
}
// Experiments