Skip to content

Commit

Permalink
check timeout on extension host, blame extension when exceeded, #43768
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Nov 19, 2019
1 parent 2e9c9db commit eff2320
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/vs/base/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {

private _asyncDeliveryQueue?: LinkedList<[Listener<T>, Omit<T, 'waitUntil'>]>;

async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<any>) => Promise<any>): Promise<void> {
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
if (!this._listeners) {
return;
}
Expand All @@ -681,7 +681,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
throw new Error('waitUntil can NOT be called asynchronous');
}
if (promiseJoin) {
p = promiseJoin(p);
p = promiseJoin(p, typeof listener === 'function' ? listener : listener[0]);
}
thenables.push(p);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class MainThreadFileSystemEventService {
reject(new Error('timeout'));
}, timeout);

proxy.$onWillRunFileOperation(e.operation, e.target, e.source, cts.token)
proxy.$onWillRunFileOperation(e.operation, e.target, e.source, timeout, cts.token)
.then(resolve, reject)
.finally(() => clearTimeout(timeoutHandle));
});
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ export interface FileSystemEvents {

export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise<any>;
$onWillRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise<any>;
$onDidRunFileOperation(operation: files.FileOperation, target: UriComponents, source: UriComponents | undefined): void;
}

Expand Down
21 changes: 14 additions & 7 deletions src/vs/workbench/api/common/extHostFileSystemEventService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { FileOperation } from 'vs/platform/files/common/files';
import { flatten } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';

class FileSystemWatcher implements vscode.FileSystemWatcher {

Expand Down Expand Up @@ -121,6 +122,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ

constructor(
mainContext: IMainContext,
private readonly _logService: ILogService,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors)
) {
Expand Down Expand Up @@ -177,32 +179,37 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
};
}

async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, token: CancellationToken): Promise<any> {
async $onWillRunFileOperation(operation: FileOperation, target: UriComponents, source: UriComponents | undefined, timeout: number, token: CancellationToken): Promise<any> {
switch (operation) {
case FileOperation.MOVE:
await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, token);
await this._fireWillEvent(this._onWillRenameFile, { files: [{ oldUri: URI.revive(source!), newUri: URI.revive(target) }] }, timeout, token);
break;
case FileOperation.DELETE:
await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, token);
await this._fireWillEvent(this._onWillDeleteFile, { files: [URI.revive(target)] }, timeout, token);
break;
case FileOperation.CREATE:
await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, token);
await this._fireWillEvent(this._onWillCreateFile, { files: [URI.revive(target)] }, timeout, token);
break;
default:
//ignore, dont send
}
}

private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, token: CancellationToken): Promise<any> {
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<any> {

const edits: WorkspaceEdit[] = [];

await emitter.fireAsync(data, token, async p => {
await emitter.fireAsync(data, token, async (thenable, listener: IExtensionListener<E>) => {
// ignore all results except for WorkspaceEdits. Those are stored in an array.
const result = await Promise.resolve(p);
const now = Date.now();
const result = await Promise.resolve(thenable);
if (result instanceof WorkspaceEdit) {
edits.push(result);
}

if (Date.now() - now > timeout) {
this._logService.warn('SLOW file-participant', listener.extension?.identifier);
}
});

if (token.isCancellationRequested) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as assert from 'assert';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { NullLogService } from 'vs/platform/log/common/log';

suite('ExtHostFileSystemEventService', () => {

Expand All @@ -17,12 +18,12 @@ suite('ExtHostFileSystemEventService', () => {
assertRegistered: undefined!
};

const watcher1 = new ExtHostFileSystemEventService(protocol, undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);
const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);
assert.equal(watcher1.ignoreChangeEvents, false);
assert.equal(watcher1.ignoreCreateEvents, false);
assert.equal(watcher1.ignoreDeleteEvents, false);

const watcher2 = new ExtHostFileSystemEventService(protocol, undefined!).createFileSystemWatcher('**/somethingBoring', true, true, true);
const watcher2 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingBoring', true, true, true);
assert.equal(watcher2.ignoreChangeEvents, true);
assert.equal(watcher2.ignoreCreateEvents, true);
assert.equal(watcher2.ignoreDeleteEvents, true);
Expand Down

0 comments on commit eff2320

Please sign in to comment.