Skip to content

Commit e9733de

Browse files
authored
Merge pull request microsoft#259350 from microsoft/tyriar/258512__259342__259339
Prevent duplicate getExecutablesInPath calls in parallel
2 parents 2eb834d + 4752734 commit e9733de

File tree

1 file changed

+36
-4
lines changed

1 file changed

+36
-4
lines changed

extensions/terminal-suggest/src/env/pathExecutableCache.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,23 @@ import { TerminalShellType } from '../terminalSuggestMain';
1616

1717
const isWindows = osIsWindows();
1818

19+
export interface IExecutablesInPath {
20+
completionResources: Set<ICompletionResource> | undefined;
21+
labels: Set<string> | undefined;
22+
}
23+
1924
export class PathExecutableCache implements vscode.Disposable {
2025
private _disposables: vscode.Disposable[] = [];
2126

2227
private _cachedWindowsExeExtensions: { [key: string]: boolean | undefined } | undefined;
2328
private _cachedExes: Map<string, Set<ICompletionResource> | undefined> = new Map();
2429

30+
private _inProgressRequest: {
31+
env: ITerminalEnvironment;
32+
shellType: TerminalShellType | undefined;
33+
promise: Promise<IExecutablesInPath | undefined>;
34+
} | undefined;
35+
2536
constructor() {
2637
if (isWindows) {
2738
this._cachedWindowsExeExtensions = vscode.workspace.getConfiguration(SettingsIds.SuggestPrefix).get(SettingsIds.CachedWindowsExecutableExtensionsSuffixOnly);
@@ -41,15 +52,36 @@ export class PathExecutableCache implements vscode.Disposable {
4152
}
4253

4354
refresh(directory?: string): void {
44-
console.trace('clear cache');
4555
if (directory) {
4656
this._cachedExes.delete(directory);
4757
} else {
4858
this._cachedExes.clear();
4959
}
5060
}
5161

52-
async getExecutablesInPath(env: ITerminalEnvironment = process.env, shellType?: TerminalShellType): Promise<{ completionResources: Set<ICompletionResource> | undefined; labels: Set<string> | undefined } | undefined> {
62+
async getExecutablesInPath(env: ITerminalEnvironment = process.env, shellType?: TerminalShellType): Promise<IExecutablesInPath | undefined> {
63+
if (this._inProgressRequest &&
64+
this._inProgressRequest.env === env &&
65+
this._inProgressRequest.shellType === shellType
66+
) {
67+
return this._inProgressRequest.promise;
68+
}
69+
70+
const promise = this._doGetExecutablesInPath(env, shellType);
71+
72+
this._inProgressRequest = {
73+
env,
74+
shellType,
75+
promise,
76+
};
77+
78+
await promise;
79+
this._inProgressRequest = undefined;
80+
81+
return promise;
82+
}
83+
84+
private async _doGetExecutablesInPath(env: ITerminalEnvironment, shellType?: TerminalShellType): Promise<IExecutablesInPath | undefined> {
5385
// Create cache key
5486
let pathValue: string | undefined;
5587
if (shellType === TerminalShellType.GitBash) {
@@ -86,7 +118,7 @@ export class PathExecutableCache implements vscode.Disposable {
86118
} else {
87119
// Not cached, need to scan this directory
88120
promisePaths.push(pathDir);
89-
promises.push(this._getExecutablesInPath(pathDir, pathSeparator, labels));
121+
promises.push(this._getExecutablesInSinglePath(pathDir, pathSeparator, labels));
90122
}
91123
}
92124

@@ -120,7 +152,7 @@ export class PathExecutableCache implements vscode.Disposable {
120152
return { completionResources: executables, labels };
121153
}
122154

123-
private async _getExecutablesInPath(path: string, pathSeparator: string, labels: Set<string>): Promise<Set<ICompletionResource> | undefined> {
155+
private async _getExecutablesInSinglePath(path: string, pathSeparator: string, labels: Set<string>): Promise<Set<ICompletionResource> | undefined> {
124156
try {
125157
const dirExists = await fs.stat(path).then(stat => stat.isDirectory()).catch(() => false);
126158
if (!dirExists) {

0 commit comments

Comments
 (0)