Skip to content

Commit c7b890c

Browse files
committed
Add protection for infinite index rebuilding
1 parent c638464 commit c7b890c

File tree

4 files changed

+79
-32
lines changed

4 files changed

+79
-32
lines changed

src/misc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ export function reportError(err) {
8383

8484
export function getErrorReportUrl(title, description) {
8585
const errorToUrls = [
86+
[
87+
'Multiple requests to rebuild the project',
88+
'https://github.com/platformio/platformio-vscode-ide/issues/2363',
89+
],
8690
[
8791
'WindowsError: [Error 5]',
8892
'https://github.com/platformio/platformio-vscode-ide/issues/884',

src/project/indexer.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
* the root directory of this source tree.
77
*/
88

9+
import path from 'path';
910
import { runPIOCommand } from '../core';
1011

1112
export default class ProjectIndexer {
12-
static AUTO_REBUILD_DELAY = 3000;
13+
static AUTO_REBUILD_DELAY = 3; // 3 seconds
14+
static FLOOD_TIME_WINDOW = 60 * 10; // 10 minutes
15+
static FLOOD_MAX_ATTEMPTS = 30;
1316

1417
constructor(projectDir, options, observer) {
1518
this.projectDir = projectDir;
@@ -18,6 +21,8 @@ export default class ProjectIndexer {
1821

1922
this._rebuildTimeout = undefined;
2023
this._inProgress = false;
24+
this._floodStartedAt = Date.now();
25+
this._floodAttempts = 0;
2126
}
2227

2328
dispose() {
@@ -27,26 +32,51 @@ export default class ProjectIndexer {
2732
}
2833

2934
requestRebuild() {
35+
if (Date.now() - this._floodStartedAt < ProjectIndexer.FLOOD_TIME_WINDOW * 1000) {
36+
this._floodAttempts++;
37+
} else {
38+
this._floodAttempts = 0;
39+
this._floodStartedAt = Date.now();
40+
}
3041
if (this._rebuildTimeout) {
3142
clearTimeout(this._rebuildTimeout);
43+
this._rebuildTimeout = undefined;
44+
}
45+
46+
if (this._floodAttempts >= ProjectIndexer.FLOOD_MAX_ATTEMPTS) {
47+
if (
48+
this._floodAttempts === ProjectIndexer.FLOOD_MAX_ATTEMPTS &&
49+
this.options.api.onDidNotifyError
50+
) {
51+
const msg =
52+
`Multiple requests to rebuild the project "${path.basename(
53+
this.projectDir
54+
)}" index have been detected!\n` +
55+
`Automatic index rebuilding process has been terminated for ${
56+
ProjectIndexer.FLOOD_TIME_WINDOW / 60
57+
} minutes.`;
58+
this.options.api.onDidNotifyError(msg, new Error(msg));
59+
}
60+
return;
3261
}
62+
3363
this._rebuildTimeout = setTimeout(
3464
this.rebuild.bind(this),
35-
ProjectIndexer.AUTO_REBUILD_DELAY
65+
ProjectIndexer.AUTO_REBUILD_DELAY * 1000
3666
);
3767
}
3868

3969
rebuild() {
4070
if (this._inProgress) {
4171
return;
4272
}
43-
return this.options.api.withWindowProgress(async () => {
73+
return this.options.api.withIndexRebuildingProgress(async () => {
4474
this._inProgress = true;
4575
try {
4676
await new Promise((resolve, reject) => {
4777
const args = ['init', '--ide', this.options.ide];
48-
if (this.observer.activeEnvName) {
49-
args.push('--environment', this.observer.activeEnvName);
78+
if (this.observer.getActiveEnvName()) {
79+
args.push('--environment', this.observer.getActiveEnvName());
5080
}
5181
runPIOCommand(
5282
args,
@@ -69,6 +99,6 @@ export default class ProjectIndexer {
6999
console.warn(err);
70100
}
71101
this._inProgress = false;
72-
}, 'PlatformIO: Rebuilding IntelliSense Index');
102+
});
73103
}
74104
}

src/project/observer.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ export default class ProjectObserver {
4242
if (this._updateDirWatchersTimeout) {
4343
clearTimeout(this._updateDirWatchersTimeout);
4444
}
45+
if (this._indexer) {
46+
this._indexer.dispose();
47+
}
4548
}
4649

4750
activate() {
4851
console.info('Activating project', this.projectDir);
49-
if (!this._indexer && this.getSetting('autoRebuild')) {
50-
this.getIndexer().rebuild();
51-
}
5252
}
5353

5454
deactivate() {
@@ -63,12 +63,27 @@ export default class ProjectObserver {
6363
this._cache.clear();
6464
}
6565

66-
get activeEnvName() {
66+
rebuildIndex({ force = false, delayed = false } = {}) {
67+
if (!force && !this.getSetting('autoRebuild')) {
68+
return;
69+
}
70+
if (!this._indexer) {
71+
this._indexer = new ProjectIndexer(this.projectDir, this.options, this);
72+
}
73+
return delayed ? this._indexer.requestRebuild() : this._indexer.rebuild();
74+
}
75+
76+
getActiveEnvName() {
6777
return this._activeEnvName;
6878
}
6979

70-
set activeEnvName(activeEnvName) {
71-
this._activeEnvName = activeEnvName;
80+
async switchProjectEnv(name) {
81+
const validNames = (await this.getProjectEnvs()).map((item) => item.name);
82+
if (!validNames.includes(name)) {
83+
name = undefined;
84+
}
85+
this._activeEnvName = name;
86+
this.rebuildIndex({ delayed: true });
7287
}
7388

7489
async getProjectEnvs() {
@@ -111,7 +126,7 @@ export default class ProjectObserver {
111126
const lazyLoading =
112127
options.preload ||
113128
this.getSetting('autoPreloadEnvTasks') ||
114-
this.activeEnvName === name ||
129+
this._activeEnvName === name ||
115130
(await this.getProjectEnvs()).length === 1;
116131
if (!lazyLoading) {
117132
return undefined;
@@ -134,28 +149,19 @@ export default class ProjectObserver {
134149
return this._cache.get(cacheKey);
135150
}
136151

137-
getIndexer() {
138-
if (!this._indexer) {
139-
this._indexer = new ProjectIndexer(this.projectDir, this.options, this);
140-
}
141-
return this._indexer;
142-
}
143-
144-
rebuildIndex() {
145-
return this.getIndexer().rebuild();
146-
}
147-
148152
onDidChangeProjectConfig() {
149153
this.resetCache();
154+
// reset to `undefined` if env was removed from conf
155+
// rebuildIndex
156+
this.switchProjectEnv(this._activeEnvName);
157+
this.requestUpdateDirWatchers();
150158
if ((this.options.api || {}).onDidChangeProjectConfig) {
151159
this.options.api.onDidChangeProjectConfig(this.projectDir);
152160
}
153-
this.getIndexer().requestRebuild();
154-
this.requestUpdateDirWatchers();
155161
}
156162

157163
onDidChangeLibDirs() {
158-
this.getIndexer().requestRebuild();
164+
this.rebuildIndex({ delayed: true });
159165
}
160166

161167
setupFSWatchers() {

src/project/pool.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ export default class ProjectPool {
2020
return this._activeProjectDir;
2121
}
2222

23+
getActiveObserver() {
24+
return this._activeProjectDir
25+
? this.getObserver(this._activeProjectDir)
26+
: undefined;
27+
}
28+
2329
getObserver(projectDir) {
30+
if (!projectDir) {
31+
return undefined;
32+
}
2433
let observer = this._observers.find(
2534
(observer) => observer.projectDir === projectDir
2635
);
@@ -40,11 +49,9 @@ export default class ProjectPool {
4049
this._observers
4150
.filter((observer) => observer.projectDir !== projectDir)
4251
.forEach((observer) => observer.deactivate());
43-
this.getObserver(projectDir).activate();
44-
}
45-
46-
rebuildIndex(projectDir) {
47-
return this.getObserver(projectDir).rebuildIndex();
52+
const observer = this.getObserver(projectDir);
53+
observer.activate();
54+
return observer;
4855
}
4956

5057
dispose() {

0 commit comments

Comments
 (0)