Skip to content

Commit be89df8

Browse files
committed
Add busy tasks and check busy tasks in debug
to prevent 'stale' prelaunch task results Fixes microsoft#82076
1 parent 59abaab commit be89df8

6 files changed

Lines changed: 58 additions & 15 deletions

File tree

src/vs/workbench/contrib/debug/browser/debugService.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -768,9 +768,27 @@ export class DebugService implements IDebugService {
768768

769769
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
770770
let taskStarted = false;
771-
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(tasks => {
771+
const inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
772+
// When a task isBackground it will go inactive when it is safe to launch.
773+
// But when a background task is terminated by the user, it will also fire an inactive event.
774+
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
775+
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
776+
return (e.kind === TaskEventKind.Inactive
777+
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
778+
&& e.taskId === task._id;
779+
}, this.taskService.onDidStateChange)(e => {
780+
taskStarted = true;
781+
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
782+
}));
783+
784+
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
772785
if (tasks.filter(t => t._id === task._id).length) {
773-
// task is already running - nothing to do.
786+
// Check that the task isn't busy and if it is, wait for it
787+
const busyTasks = await this.taskService.getBusyTasks();
788+
if (busyTasks.filter(t => t._id === task._id).length) {
789+
return inactivePromise;
790+
}
791+
// task is already running and isn't busy - nothing to do.
774792
return Promise.resolve(null);
775793
}
776794
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
@@ -780,18 +798,7 @@ export class DebugService implements IDebugService {
780798
});
781799
const taskPromise = this.taskService.run(task);
782800
if (task.configurationProperties.isBackground) {
783-
return new Promise((c, e) => once(e => {
784-
// When a task isBackground it will go inactive when it is safe to launch.
785-
// But when a background task is terminated by the user, it will also fire an inactive event.
786-
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
787-
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
788-
return (e.kind === TaskEventKind.Inactive
789-
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
790-
&& e.taskId === task._id;
791-
}, this.taskService.onDidStateChange)(e => {
792-
taskStarted = true;
793-
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
794-
}));
801+
return inactivePromise;
795802
}
796803

797804
return taskPromise;

src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
569569
return Promise.resolve(this._taskSystem.getActiveTasks());
570570
}
571571

572+
public getBusyTasks(): Promise<Task[]> {
573+
if (!this._taskSystem) {
574+
return Promise.resolve([]);
575+
}
576+
return Promise.resolve(this._taskSystem.getBusyTasks());
577+
}
578+
572579
public getRecentlyUsedTasks(): LinkedMap<string, string> {
573580
if (this._recentlyUsedTasks) {
574581
return this._recentlyUsedTasks;

src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export class TerminalTaskSystem implements ITaskSystem {
152152
};
153153

154154
private activeTasks: IStringDictionary<ActiveTerminalData>;
155+
private busyTasks: IStringDictionary<Task>;
155156
private terminals: IStringDictionary<TerminalData>;
156157
private idleTaskTerminals: LinkedMap<string, string>;
157158
private sameTaskTerminals: IStringDictionary<string>;
@@ -180,6 +181,7 @@ export class TerminalTaskSystem implements ITaskSystem {
180181
) {
181182

182183
this.activeTasks = Object.create(null);
184+
this.busyTasks = Object.create(null);
183185
this.terminals = Object.create(null);
184186
this.idleTaskTerminals = new LinkedMap<string, string>();
185187
this.sameTaskTerminals = Object.create(null);
@@ -280,6 +282,10 @@ export class TerminalTaskSystem implements ITaskSystem {
280282
return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task);
281283
}
282284

285+
public getBusyTasks(): Task[] {
286+
return Object.keys(this.busyTasks).map(key => this.busyTasks[key]);
287+
}
288+
283289
public customExecutionComplete(task: Task, result: number): Promise<void> {
284290
let activeTerminal = this.activeTasks[task.getMapKey()];
285291
if (!activeTerminal) {
@@ -533,12 +539,17 @@ export class TerminalTaskSystem implements ITaskSystem {
533539
}
534540
const toDispose = new DisposableStore();
535541
let eventCounter: number = 0;
542+
const mapKey = task.getMapKey();
536543
toDispose.add(watchingProblemMatcher.onDidStateChange((event) => {
537544
if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) {
538545
eventCounter++;
546+
this.busyTasks[mapKey] = task;
539547
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
540548
} else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) {
541549
eventCounter--;
550+
if (this.busyTasks[mapKey]) {
551+
delete this.busyTasks[mapKey];
552+
}
542553
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
543554
if (eventCounter === 0) {
544555
if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity &&
@@ -597,6 +608,9 @@ export class TerminalTaskSystem implements ITaskSystem {
597608
onData.dispose();
598609
onExit.dispose();
599610
let key = task.getMapKey();
611+
if (this.busyTasks[mapKey]) {
612+
delete this.busyTasks[mapKey];
613+
}
600614
delete this.activeTasks[key];
601615
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Changed));
602616
if (exitCode !== undefined) {
@@ -656,6 +670,8 @@ export class TerminalTaskSystem implements ITaskSystem {
656670
// The process never got ready. Need to think how to handle this.
657671
});
658672
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id));
673+
const mapKey = task.getMapKey();
674+
this.busyTasks[mapKey] = task;
659675
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
660676
let problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers);
661677
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService);
@@ -709,6 +725,9 @@ export class TerminalTaskSystem implements ITaskSystem {
709725
}
710726

711727
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode));
728+
if (this.busyTasks[mapKey]) {
729+
delete this.busyTasks[mapKey];
730+
}
712731
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task));
713732
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task));
714733
resolve({ exitCode });
@@ -1028,7 +1047,11 @@ export class TerminalTaskSystem implements ITaskSystem {
10281047
// This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
10291048
// For correct terminal re-use, the task needs to be deleted immediately.
10301049
// Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
1031-
delete this.activeTasks[task.getMapKey()];
1050+
const mapKey = task.getMapKey();
1051+
delete this.activeTasks[mapKey];
1052+
if (this.busyTasks[mapKey]) {
1053+
delete this.busyTasks[mapKey];
1054+
}
10321055
}
10331056
});
10341057
this.terminals[terminalKey] = { terminal: result, lastTask: taskKey, group };

src/vs/workbench/contrib/tasks/common/taskService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export interface ITaskService {
6262
inTerminal(): boolean;
6363
isActive(): Promise<boolean>;
6464
getActiveTasks(): Promise<Task[]>;
65+
getBusyTasks(): Promise<Task[]>;
6566
restart(task: Task): void;
6667
terminate(task: Task): Promise<TaskTerminateResponse>;
6768
terminateAll(): Promise<TaskTerminateResponse[]>;

src/vs/workbench/contrib/tasks/common/taskSystem.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export interface ITaskSystem {
133133
isActive(): Promise<boolean>;
134134
isActiveSync(): boolean;
135135
getActiveTasks(): Task[];
136+
getBusyTasks(): Task[];
136137
canAutoTerminate(): boolean;
137138
terminate(task: Task): Promise<TaskTerminateResponse>;
138139
terminateAll(): Promise<TaskTerminateResponse[]>;

src/vs/workbench/contrib/tasks/node/processTaskSystem.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export class ProcessTaskSystem implements ITaskSystem {
9090
return result;
9191
}
9292

93+
public getBusyTasks(): Task[] {
94+
return this.getActiveTasks();
95+
}
96+
9397
public run(task: Task): ITaskExecuteResult {
9498
if (this.activeTask) {
9599
return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.configurationProperties.isBackground! }, promise: this.activeTaskPromise! };

0 commit comments

Comments
 (0)