Skip to content

Commit

Permalink
#56137 - remove more TPromise#cancel from search
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Sep 4, 2018
1 parent 5f513e7 commit 497ea88
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 156 deletions.
5 changes: 3 additions & 2 deletions src/vs/platform/search/common/search.ts
Expand Up @@ -13,6 +13,7 @@ import { URI as uri, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IFilesConfiguration } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { CancellationToken } from 'vs/base/common/cancellation';

export const VIEW_ID = 'workbench.view.search';

Expand All @@ -24,7 +25,7 @@ export const ISearchService = createDecorator<ISearchService>('searchService');
*/
export interface ISearchService {
_serviceBrand: any;
search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise<ISearchComplete>;
search(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise<ISearchComplete>;
extendQuery(query: ISearchQuery): void;
clearCache(cacheKey: string): TPromise<void>;
registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable;
Expand Down Expand Up @@ -55,7 +56,7 @@ export enum SearchProviderType {
}

export interface ISearchResultProvider {
search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise<ISearchComplete>;
search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise<ISearchComplete>;
clearCache(cacheKey: string): TPromise<void>;
}

Expand Down
37 changes: 16 additions & 21 deletions src/vs/workbench/api/electron-browser/mainThreadSearch.ts
Expand Up @@ -13,6 +13,7 @@ import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISea
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol';
import { CancellationToken } from 'vs/base/common/cancellation';

@extHostNamedCustomer(MainContext.MainThreadSearch)
export class MainThreadSearch implements MainThreadSearchShape {
Expand Down Expand Up @@ -106,34 +107,28 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
dispose(this._registrations);
}

search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise<ISearchComplete> {

search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise<ISearchComplete> {
if (isFalsyOrEmpty(query.folderQueries)) {
return TPromise.as(undefined);
}

let outer: TPromise;

return new TPromise((resolve, reject) => {
const search = new SearchOperation(onProgress);
this._searches.set(search.id, search);

const search = new SearchOperation(onProgress);
this._searches.set(search.id, search);
const searchP = query.type === QueryType.File
? this._proxy.$provideFileSearchResults(this._handle, search.id, query)
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, query);

outer = query.type === QueryType.File
? this._proxy.$provideFileSearchResults(this._handle, search.id, query)
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, query);
if (token) {
token.onCancellationRequested(() => searchP.cancel());
}

outer.then((result: ISearchCompleteStats) => {
this._searches.delete(search.id);
resolve(({ results: values(search.matches), stats: result.stats, limitHit: result.limitHit }));
}, err => {
this._searches.delete(search.id);
reject(err);
});
}, () => {
if (outer) {
outer.cancel();
}
return searchP.then((result: ISearchCompleteStats) => {
this._searches.delete(search.id);
return { results: values(search.matches), stats: result.stats, limitHit: result.limitHit };
}, err => {
this._searches.delete(search.id);
return TPromise.wrapError(err);
});
}

Expand Down
49 changes: 30 additions & 19 deletions src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts
Expand Up @@ -24,12 +24,13 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol';
import { CancellationTokenSource } from 'vs/base/common/cancellation';

@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {

private readonly _toDispose: IDisposable[] = [];
private readonly _activeSearches: { [id: number]: TPromise<any> } = Object.create(null);
private readonly _activeCancelTokens: { [id: number]: CancellationTokenSource } = Object.create(null);
private readonly _proxy: ExtHostWorkspaceShape;

constructor(
Expand All @@ -51,9 +52,9 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
dispose(): void {
dispose(this._toDispose);

for (let requestId in this._activeSearches) {
const search = this._activeSearches[requestId];
search.cancel();
for (let requestId in this._activeCancelTokens) {
const tokenSource = this._activeCancelTokens[requestId];
tokenSource.cancel();
}
}

Expand Down Expand Up @@ -157,7 +158,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {

this._searchService.extendQuery(query);

const search = this._searchService.search(query).then(result => {
const tokenSource = new CancellationTokenSource();
const search = this._searchService.search(query, tokenSource.token).then(result => {
return result.results.map(m => m.resource);
}, err => {
if (!isPromiseCanceledError(err)) {
Expand All @@ -166,8 +168,11 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
return undefined;
});

this._activeSearches[requestId] = search;
const onDone = () => delete this._activeSearches[requestId];
this._activeCancelTokens[requestId] = tokenSource;
const onDone = () => {
tokenSource.dispose();
delete this._activeCancelTokens[requestId];
};
search.then(onDone, onDone);

return search;
Expand All @@ -186,51 +191,57 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
};

const search = this._searchService.search(query, onProgress).then(
const tokenSource = new CancellationTokenSource();
const search = this._searchService.search(query, tokenSource.token, onProgress).then(
() => {
delete this._activeSearches[requestId];
return null;
},
err => {
delete this._activeSearches[requestId];
if (!isPromiseCanceledError(err)) {
return TPromise.wrapError(err);
}

return undefined;
});

this._activeSearches[requestId] = search;
this._activeCancelTokens[requestId] = tokenSource;
const onDone = () => {
tokenSource.dispose();
delete this._activeCancelTokens[requestId];
};
search.then(onDone, onDone);

return search;
}

$checkExists(query: ISearchQuery, requestId: number): TPromise<boolean> {
query.exists = true;
const search = this._searchService.search(query).then(

const tokenSource = new CancellationTokenSource();
const search = this._searchService.search(query, tokenSource.token).then(
result => {
delete this._activeSearches[requestId];
delete this._activeCancelTokens[requestId];
return result.limitHit;
},
err => {
delete this._activeSearches[requestId];
delete this._activeCancelTokens[requestId];
if (!isPromiseCanceledError(err)) {
return TPromise.wrapError(err);
}

return undefined;
});

this._activeSearches[requestId] = search;
this._activeCancelTokens[requestId] = tokenSource;

return search;
}

$cancelSearch(requestId: number): Thenable<boolean> {
const search = this._activeSearches[requestId];
if (search) {
delete this._activeSearches[requestId];
search.cancel();
const tokenSource = this._activeCancelTokens[requestId];
if (tokenSource) {
delete this._activeCancelTokens[requestId];
tokenSource.cancel();
return TPromise.as(true);
}
return undefined;
Expand Down
59 changes: 23 additions & 36 deletions src/vs/workbench/api/node/extHostSearch.ts
Expand Up @@ -98,19 +98,15 @@ export class ExtHostSearch implements ExtHostSearchShape {
});
} else {
const indexProvider = this._fileIndexProvider.get(handle);
if (indexProvider) {
return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}).then(null, err => {
if (!isPromiseCanceledError(err)) {
throw err;
}
return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}).then(null, err => {
if (!isPromiseCanceledError(err)) {
throw err;
}

return null;
});
} else {
throw new Error('something went wrong');
}
return null;
});
}
}

Expand Down Expand Up @@ -300,36 +296,31 @@ class BatchedCollector<T> {

class TextSearchEngine {

private activeCancellationTokens = new Set<CancellationTokenSource>();
private collector: TextSearchResultsCollector;

private isLimitHit: boolean;
private resultCount = 0;
private isCanceled: boolean;

constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) {
}

public cancel(): void {
this.isCanceled = true;
this.activeCancellationTokens.forEach(t => t.cancel());
this.activeCancellationTokens = new Set();
}

public search(onProgress: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> {
const folderQueries = this.config.folderQueries;
const tokenSource = new CancellationTokenSource();

return new TPromise<ISearchCompleteStats>((resolve, reject) => {
this.collector = new TextSearchResultsCollector(onProgress);

let isCanceled = false;
const onResult = (match: vscode.TextSearchResult, folderIdx: number) => {
if (this.isCanceled) {
if (isCanceled) {
return;
}

if (this.resultCount >= this.config.maxResults) {
this.isLimitHit = true;
this.cancel();
isCanceled = true;
tokenSource.cancel();
}

if (!this.isLimitHit) {
Expand All @@ -340,8 +331,9 @@ class TextSearchEngine {

// For each root folder
TPromise.join(folderQueries.map((fq, i) => {
return this.searchInFolder(fq, r => onResult(r, i));
return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token);
})).then(() => {
tokenSource.dispose();
this.collector.flush();
resolve({
limitHit: this.isLimitHit,
Expand All @@ -350,17 +342,20 @@ class TextSearchEngine {
}
});
}, (errs: Error[]) => {
tokenSource.dispose();
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];

reject(new Error(errMsg));
});
}, () => {
// From IPC
tokenSource.cancel();
});
}

private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void): TPromise<void> {
let cancellation = new CancellationTokenSource();
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise<void> {
return new TPromise((resolve, reject) => {

const queryTester = new QueryGlobTester(this.config, folderQuery);
Expand All @@ -385,22 +380,14 @@ class TextSearchEngine {
const searchOptions = this.getSearchOptionsForFolder(folderQuery);
new TPromise(resolve => process.nextTick(resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
return this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, cancellation.token);
return this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token);
})
.then(() => {
this.activeCancellationTokens.delete(cancellation);
return TPromise.join(testingPs);
})
.then(
() => {
cancellation.dispose();
resolve(null);
},
err => {
cancellation.dispose();
reject(err);
});
() => resolve(null),
reject);
});
}

Expand Down
3 changes: 1 addition & 2 deletions src/vs/workbench/parts/search/browser/openFileHandler.ts
Expand Up @@ -172,8 +172,7 @@ export class OpenFileHandler extends QuickOpenHandler {
return TPromise.wrap(<ISearchComplete>{ results: [{ resource: result }] });
}

// TODO@Rob support cancellation
return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions));
return this.searchService.search(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token);
}).then(complete => {
const results: QuickOpenEntry[] = [];
for (let i = 0; i < complete.results.length; i++) {
Expand Down

0 comments on commit 497ea88

Please sign in to comment.