Fix #16760 - account for worker process timing out during a search

This commit is contained in:
roblou
2016-12-07 13:40:43 -08:00
parent 4f37dc1273
commit 15f1e56f7e
3 changed files with 24 additions and 29 deletions
@@ -12,7 +12,7 @@ import { IProgress } from 'vs/platform/search/common/search';
import { FileWalker } from 'vs/workbench/services/search/node/fileSearch';
import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEngine } from './search';
import { ISearchWorker, ISearchWorkerConfig } from './worker/searchWorkerIpc';
import { ISearchWorker } from './worker/searchWorkerIpc';
import { ITextSearchWorkerProvider } from './textSearchWorkerProvider';
export class Engine implements ISearchEngine<ISerializedFileMatch> {
@@ -55,8 +55,7 @@ export class Engine implements ISearchEngine<ISerializedFileMatch> {
initializeWorkers(): void {
this.workers.forEach(w => {
const config: ISearchWorkerConfig = { pattern: this.config.contentPattern, fileEncoding: this.config.fileEncoding };
w.initialize(config)
w.initialize()
.then(null, onUnexpectedError);
});
}
@@ -94,7 +93,8 @@ export class Engine implements ISearchEngine<ISerializedFileMatch> {
this.nextWorker = (this.nextWorker + 1) % this.workers.length;
const maxResults = this.config.maxResults && (this.config.maxResults - this.numResults);
worker.search({ absolutePaths: batch, maxResults }).then(result => {
const searchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding: this.config.fileEncoding };
worker.search(searchArgs).then(result => {
if (!result || this.limitReached || this.isCanceled) {
return unwind(batchBytes);
}
@@ -18,7 +18,7 @@ import { ILineMatch } from 'vs/platform/search/common/search';
import { UTF16le, UTF16be, UTF8, UTF8_with_bom, encodingExists, decode } from 'vs/base/node/encoding';
import { detectMimeAndEncodingFromBuffer } from 'vs/base/node/mime';
import { ISearchWorker, ISearchWorkerConfig, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc';
import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc';
interface ReadLinesOptions {
bufferLength: number;
@@ -36,20 +36,24 @@ function onError(error: any): void {
export class SearchWorkerManager implements ISearchWorker {
private currentSearchEngine: SearchWorkerEngine;
initialize(config: ISearchWorkerConfig): TPromise<void> {
this.currentSearchEngine = new SearchWorkerEngine(config);
initialize(): TPromise<void> {
this.currentSearchEngine = new SearchWorkerEngine();
return TPromise.wrap<void>(undefined);
}
cancel(): TPromise<void> {
// Cancel the current search. It will stop searching and close its open files.
this.currentSearchEngine.cancel();
if (this.currentSearchEngine) {
this.currentSearchEngine.cancel();
}
return TPromise.wrap<void>(null);
}
search(args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult> {
if (!this.currentSearchEngine) {
return TPromise.wrapError(new Error('SearchWorker is not initialized'));
// Worker timed out during search
this.initialize();
}
return this.currentSearchEngine.searchBatch(args);
@@ -63,27 +67,21 @@ interface IFileSearchResult {
}
export class SearchWorkerEngine {
private contentPattern: RegExp;
private fileEncoding: string;
private nextSearch = TPromise.wrap(null);
private isCanceled = false;
constructor(config: ISearchWorkerConfig) {
this.contentPattern = strings.createRegExp(config.pattern.pattern, config.pattern.isRegExp, { matchCase: config.pattern.isCaseSensitive, wholeWord: config.pattern.isWordMatch, multiline: false, global: true });
this.fileEncoding = encodingExists(config.fileEncoding) ? config.fileEncoding : UTF8;
}
/**
* Searches some number of the given paths concurrently, and starts searches in other paths when those complete.
*/
searchBatch(args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult> {
const contentPattern = strings.createRegExp(args.pattern.pattern, args.pattern.isRegExp, { matchCase: args.pattern.isCaseSensitive, wholeWord: args.pattern.isWordMatch, multiline: false, global: true });
const fileEncoding = encodingExists(args.fileEncoding) ? args.fileEncoding : UTF8;
return this.nextSearch =
this.nextSearch.then(() => this._searchBatch(args));
this.nextSearch.then(() => this._searchBatch(args, contentPattern, fileEncoding));
}
private _searchBatch(args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult> {
private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): TPromise<ISearchWorkerSearchResult> {
if (this.isCanceled) {
return TPromise.wrap(null);
}
@@ -97,7 +95,7 @@ export class SearchWorkerEngine {
// Search in the given path, and when it's finished, search in the next path in absolutePaths
const startSearchInFile = (absolutePath: string): TPromise<void> => {
return this.searchInFile(absolutePath, this.contentPattern, this.fileEncoding, args.maxResults && (args.maxResults - result.numMatches)).then(fileResult => {
return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches)).then(fileResult => {
// Finish early if search is canceled
if (this.isCanceled) {
return;
@@ -11,12 +11,9 @@ import { ISerializedFileMatch } from '../search';
import { IPatternInfo } from 'vs/platform/search/common/search';
import { SearchWorkerManager } from './searchWorker';
export interface ISearchWorkerConfig {
export interface ISearchWorkerSearchArgs {
pattern: IPatternInfo;
fileEncoding: string;
}
export interface ISearchWorkerSearchArgs {
absolutePaths: string[];
maxResults?: number;
}
@@ -28,13 +25,13 @@ export interface ISearchWorkerSearchResult {
}
export interface ISearchWorker {
initialize(config: ISearchWorkerConfig): TPromise<void>;
initialize(): TPromise<void>;
search(args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult>;
cancel(): TPromise<void>;
}
export interface ISearchWorkerChannel extends IChannel {
call(command: 'initialize', config: ISearchWorkerConfig): TPromise<void>;
call(command: 'initialize'): TPromise<void>;
call(command: 'search', args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult>;
call(command: 'cancel'): TPromise<void>;
call(command: string, arg?: any): TPromise<any>;
@@ -46,7 +43,7 @@ export class SearchWorkerChannel implements ISearchWorkerChannel {
call(command: string, arg?: any): TPromise<any> {
switch (command) {
case 'initialize': return this.worker.initialize(arg);
case 'initialize': return this.worker.initialize();
case 'search': return this.worker.search(arg);
case 'cancel': return this.worker.cancel();
}
@@ -56,8 +53,8 @@ export class SearchWorkerChannel implements ISearchWorkerChannel {
export class SearchWorkerChannelClient implements ISearchWorker {
constructor(private channel: ISearchWorkerChannel) { }
initialize(config: ISearchWorkerConfig): TPromise<void> {
return this.channel.call('initialize', config);
initialize(): TPromise<void> {
return this.channel.call('initialize');
}
search(args: ISearchWorkerSearchArgs): TPromise<ISearchWorkerSearchResult> {