Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ export class CodeApplication extends Disposable {
// Local Files
const diskFileSystemProvider = this.fileService.getProvider(Schemas.file);
assertType(diskFileSystemProvider instanceof DiskFileSystemProvider);
const fileSystemProviderChannel = disposables.add(new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService, this.environmentMainService));
const fileSystemProviderChannel = disposables.add(new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService, this.environmentMainService, this.configurationService));
mainProcessElectronServer.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel);
sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel));

Expand Down
62 changes: 33 additions & 29 deletions src/vs/platform/files/common/diskFileSystemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable implemen

constructor(
protected readonly logService: ILogService,
private readonly options?: IDiskFileSystemProviderOptions
protected options?: IDiskFileSystemProviderOptions
) {
super();
}
Expand All @@ -71,16 +71,7 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable implemen
private readonly universalWatchRequestDelayer = this._register(new ThrottledDelayer<void>(0));

private watchUniversal(resource: URI, opts: IWatchOptions): IDisposable {

// Add to list of paths to watch universally
const request: IUniversalWatchRequest = {
path: this.toWatchPath(resource),
excludes: opts.excludes,
includes: opts.includes,
recursive: opts.recursive,
filter: opts.filter,
correlationId: opts.correlationId
};
const request = this.toWatchRequest(resource, opts);
const remove = insert(this.universalWatchRequests, request);

// Trigger update
Expand All @@ -96,6 +87,37 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable implemen
});
}

private toWatchRequest(resource: URI, opts: IWatchOptions): IUniversalWatchRequest {
const request: IUniversalWatchRequest = {
path: this.toWatchPath(resource),
excludes: opts.excludes,
includes: opts.includes,
recursive: opts.recursive,
filter: opts.filter,
correlationId: opts.correlationId
};

if (isRecursiveWatchRequest(request)) {

// Adjust for polling
const usePolling = this.options?.watcher?.recursive?.usePolling;
if (usePolling === true) {
request.pollingInterval = this.options?.watcher?.recursive?.pollingInterval ?? 5000;
} else if (Array.isArray(usePolling)) {
if (usePolling.includes(request.path)) {
request.pollingInterval = this.options?.watcher?.recursive?.pollingInterval ?? 5000;
}
}

// Adjust for next version
if (this.options?.watcher?.recursive?.useNext) {
request.useNext = true;
}
}

return request;
}

private refreshUniversalWatchers(): void {

// Buffer requests for universal watching to decide on right watcher
Expand All @@ -121,24 +143,6 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable implemen
}));
}

// Adjust for polling
const usePolling = this.options?.watcher?.recursive?.usePolling;
if (usePolling === true) {
for (const request of this.universalWatchRequests) {
if (isRecursiveWatchRequest(request)) {
request.pollingInterval = this.options?.watcher?.recursive?.pollingInterval ?? 5000;
}
}
} else if (Array.isArray(usePolling)) {
for (const request of this.universalWatchRequests) {
if (isRecursiveWatchRequest(request)) {
if (usePolling.includes(request.path)) {
request.pollingInterval = this.options?.watcher?.recursive?.pollingInterval ?? 5000;
}
}
}
}

// Ask to watch the provided paths
return this.universalWatcher.watch(this.universalWatchRequests);
}
Expand Down
10 changes: 10 additions & 0 deletions src/vs/platform/files/common/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ export interface IRecursiveWatchRequest extends IWatchRequest {
* be used in any other case.
*/
pollingInterval?: number;

/**
* TODO@bpasero Temporary flag to test the new watcher implementation
*/
useNext?: boolean;
}

export function isRecursiveWatchRequest(request: IWatchRequest): request is IRecursiveWatchRequest {
Expand Down Expand Up @@ -169,6 +174,11 @@ export interface IRecursiveWatcherOptions {
* be used in any other case.
*/
readonly pollingInterval?: number;

/**
* TODO@bpasero Temporary flag to test the new watcher implementation
*/
readonly useNext?: boolean;
}

export interface INonRecursiveWatcher extends IWatcher {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import { AbstractDiskFileSystemProviderChannel, AbstractSessionFileWatcher, ISes
import { DefaultURITransformer, IURITransformer } from '../../../base/common/uriIpc.js';
import { IEnvironmentService } from '../../environment/common/environment.js';
import { toErrorMessage } from '../../../base/common/errorMessage.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';

export class DiskFileSystemProviderChannel extends AbstractDiskFileSystemProviderChannel<unknown> {

constructor(
provider: DiskFileSystemProvider,
logService: ILogService,
private readonly environmentService: IEnvironmentService
private readonly environmentService: IEnvironmentService,
private readonly configurationService: IConfigurationService
) {
super(provider, logService);
}
Expand Down Expand Up @@ -57,7 +59,7 @@ export class DiskFileSystemProviderChannel extends AbstractDiskFileSystemProvide
//#region File Watching

protected createSessionFileWatcher(uriTransformer: IURITransformer, emitter: Emitter<IFileChange[] | string>): ISessionFileWatcher {
return new SessionFileWatcher(uriTransformer, emitter, this.logService, this.environmentService);
return new SessionFileWatcher(uriTransformer, emitter, this.logService, this.environmentService, this.configurationService);
}

//#endregion
Expand Down
10 changes: 8 additions & 2 deletions src/vs/platform/files/node/diskFileSystemProviderServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IStat, IFileReadStreamOptions, IFileWriteOptions, IFileOpenOptions, IFi
import { CancellationTokenSource } from '../../../base/common/cancellation.js';
import { IRecursiveWatcherOptions } from '../common/watcher.js';
import { IEnvironmentService } from '../../environment/common/environment.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';

export interface ISessionFileWatcher extends IDisposable {
watch(req: number, resource: URI, opts: IWatchOptions): IDisposable;
Expand Down Expand Up @@ -278,7 +279,8 @@ export abstract class AbstractSessionFileWatcher extends Disposable implements I
private readonly uriTransformer: IURITransformer,
sessionEmitter: Emitter<IFileChange[] | string>,
private readonly logService: ILogService,
private readonly environmentService: IEnvironmentService
private readonly environmentService: IEnvironmentService,
private readonly configurationService: IConfigurationService
) {
super();

Expand All @@ -303,7 +305,11 @@ export abstract class AbstractSessionFileWatcher extends Disposable implements I
}

protected getRecursiveWatcherOptions(environmentService: IEnvironmentService): IRecursiveWatcherOptions | undefined {
return undefined; // subclasses can override
if (this.configurationService.getValue<boolean>('files.experimentalWatcherNext') === true) {
return { useNext: true, usePolling: false };
}

return undefined;
}

protected getExtraExcludes(environmentService: IEnvironmentService): string[] | undefined {
Expand Down
11 changes: 5 additions & 6 deletions src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ 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> }>());
Expand Down Expand Up @@ -293,7 +290,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
// Path checks for symbolic links / wrong casing
const { realPath, realPathDiffers, realPathLength } = this.normalizePath(request);

this.trace(`Started watching: '${realPath}' with polling interval '${pollingInterval}'`);
this.trace(`Started watching: '${realPath}' with polling interval '${pollingInterval}' and version '${request.useNext ? 'next' : 'stable'}'`);

let counter = 0;

Expand All @@ -305,6 +302,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
}

// We already ran before, check for events since
const parcelWatcherLib = request.useNext ? parcelWatcher2 : parcelWatcher;
if (counter > 1) {
const parcelEvents = await parcelWatcherLib.getEventsSince(realPath, snapshotFile, { ignore: this.addPredefinedExcludes(request.excludes), backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });

Expand Down Expand Up @@ -362,6 +360,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
const { realPath, realPathDiffers, realPathLength } = this.normalizePath(request);

try {
const parcelWatcherLib = request.useNext ? parcelWatcher2 : parcelWatcher;
const parcelWatcherInstance = await parcelWatcherLib.subscribe(realPath, (error, parcelEvents) => {
if (watcher.token.isCancellationRequested) {
return; // return early when disposed
Expand All @@ -382,7 +381,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
ignore: this.addPredefinedExcludes(watcher.request.excludes)
});

this.trace(`Started watching: '${realPath}' with backend '${ParcelWatcher.PARCEL_WATCHER_BACKEND}'`);
this.trace(`Started watching: '${realPath}' with backend '${ParcelWatcher.PARCEL_WATCHER_BACKEND}' and version '${request.useNext ? 'next' : 'stable'}'`);

instance.complete(parcelWatcherInstance);
} catch (error) {
Expand Down Expand Up @@ -862,7 +861,7 @@ export class ParcelWatcher extends BaseWatcher implements IRecursiveWatcherWithS
}

private toMessage(message: string, request?: IRecursiveWatchRequest): string {
return request ? `[File Watcher (${useParcelWatcher2 ? 'parcel-next' : 'parcel-classic'})] ${message} (path: ${request.path})` : `[File Watcher (${useParcelWatcher2 ? 'parcel-next' : 'parcel-classic'})] ${message}`;
return request ? `[File Watcher (${request.useNext ? 'parcel-next' : 'parcel-classic'})] ${message} (path: ${request.path})` : `[File Watcher ('parcel')] ${message}`;
}

protected get recursiveWatcher() { return this; }
Expand Down
6 changes: 3 additions & 3 deletions src/vs/platform/files/node/watcher/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { Disposable } from '../../../../base/common/lifecycle.js';
import { ILogMessage, IUniversalWatcher, IUniversalWatchRequest } from '../../common/watcher.js';
import { ILogMessage, isRecursiveWatchRequest, IUniversalWatcher, IUniversalWatchRequest } from '../../common/watcher.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { ParcelWatcher } from './parcel/parcelWatcher.js';
import { NodeJSWatcher } from './nodejs/nodejsWatcher.js';
Expand Down Expand Up @@ -33,13 +33,13 @@ export class UniversalWatcher extends Disposable implements IUniversalWatcher {

let error: Error | undefined;
try {
await this.recursiveWatcher.watch(requests.filter(request => request.recursive));
await this.recursiveWatcher.watch(requests.filter(request => isRecursiveWatchRequest(request)));
} catch (e) {
error = e;
}

try {
await this.nonRecursiveWatcher.watch(requests.filter(request => !request.recursive));
await this.nonRecursiveWatcher.watch(requests.filter(request => !isRecursiveWatchRequest(request)));
} catch (e) {
if (!error) {
error = e;
Expand Down
12 changes: 6 additions & 6 deletions src/vs/platform/files/node/watcher/watcherStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,26 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IUniversalWatchRequest, requestFilterToString } from '../../common/watcher.js';
import { INonRecursiveWatchRequest, IRecursiveWatchRequest, isRecursiveWatchRequest, IUniversalWatchRequest, requestFilterToString } from '../../common/watcher.js';
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,
nonRecursiveWatcher: NodeJSWatcher
): string {
const lines: string[] = [];

const allRecursiveRequests = sortByPathPrefix(requests.filter(request => request.recursive));
const allRecursiveRequests = sortByPathPrefix(requests.filter(request => isRecursiveWatchRequest(request)));
const nonSuspendedRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === false);
const suspendedPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === 'polling');
const suspendedNonPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === true);

const recursiveRequestsStatus = computeRequestStatus(allRecursiveRequests, recursiveWatcher);
const recursiveWatcherStatus = computeRecursiveWatchStatus(recursiveWatcher);

const allNonRecursiveRequests = sortByPathPrefix(requests.filter(request => !request.recursive));
const allNonRecursiveRequests = sortByPathPrefix(requests.filter(request => !isRecursiveWatchRequest(request)));
const nonSuspendedNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === false);
const suspendedPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === 'polling');
const suspendedNonPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === true);
Expand Down Expand Up @@ -61,7 +59,7 @@ export function computeStats(
fillNonRecursiveWatcherStats(nonRecursiveWatcheLines, nonRecursiveWatcher);
lines.push(...alignTextColumns(nonRecursiveWatcheLines));

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`;
return allRecursiveRequests.some(r => r.useNext) ? `\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[]) {
Expand Down Expand Up @@ -142,6 +140,8 @@ function computeNonRecursiveWatchStatus(nonRecursiveWatcher: NodeJSWatcher): { a
return { active, failed, reusing };
}

function sortByPathPrefix(requests: IRecursiveWatchRequest[]): IRecursiveWatchRequest[];
function sortByPathPrefix(requests: INonRecursiveWatchRequest[]): INonRecursiveWatchRequest[];
function sortByPathPrefix(requests: IUniversalWatchRequest[]): IUniversalWatchRequest[];
function sortByPathPrefix(requests: INodeJSWatcherInstance[]): INodeJSWatcherInstance[];
function sortByPathPrefix(requests: ParcelWatcherInstance[]): ParcelWatcherInstance[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ 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 { IWindowUtilityProcessConfiguration, WindowUtilityProcess } from './utilityProcess.js';
import { 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');

Expand All @@ -33,8 +32,7 @@ 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,
@IConfigurationService private readonly configurationService: IConfigurationService
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
) {
super();
}
Expand All @@ -52,7 +50,7 @@ export class UtilityProcessWorkerMainService extends Disposable implements IUtil
}

// Create new worker
const worker = new UtilityProcessWorker(this.logService, this.windowsMainService, this.telemetryService, this.lifecycleMainService, this.configurationService, configuration);
const worker = new UtilityProcessWorker(this.logService, this.windowsMainService, this.telemetryService, this.lifecycleMainService, configuration);
if (!worker.spawn()) {
return { reason: { code: 1, signal: 'EINVALID' } };
}
Expand Down Expand Up @@ -108,7 +106,6 @@ 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();
Expand All @@ -125,7 +122,7 @@ class UtilityProcessWorker extends Disposable {
const window = this.windowsMainService.getWindowById(this.configuration.reply.windowId);
const windowPid = window?.win?.webContents.getOSProcessId();

let configuration: IWindowUtilityProcessConfiguration = {
return this.utilityProcess.start({
type: this.configuration.process.type,
entryPoint: this.configuration.process.moduleId,
parentLifecycleBound: windowPid,
Expand All @@ -134,18 +131,7 @@ 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() {
Expand Down
Loading