Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make _activeTasks synchronous wrt _executeTask #180617

Merged
merged 14 commits into from
May 4, 2023

Conversation

markw65
Copy link
Contributor

@markw65 markw65 commented Apr 22, 2023

Fixes #180578

The problem was that after a call to _executeTask there is a delay (at least until the next tick, but potentially considerably longer) before the task is added to _activeTasks. During this period a second instance of the task could be kicked off, causing problems.

This commit fixes the issue by adding an entry (without a terminal) to _activeTasks before returning from _executeTask. For the most part though, it preserves the existing behavior - so for example getActiveTasks only returns the tasks that have terminals attached, just as it did before; and there is no change to when TaskEvent events are fired for the task.

@markw65 markw65 force-pushed the synchronous-execute-task branch from 5413180 to 39a1a6c Compare April 22, 2023 19:57
@meganrogge meganrogge added this to the May 2023 milestone Apr 24, 2023
@markw65 markw65 force-pushed the synchronous-execute-task branch 2 times, most recently from edde211 to a473d38 Compare April 24, 2023 22:25
@markw65
Copy link
Contributor Author

markw65 commented Apr 25, 2023

This still isn't right. This version once again reports false dependency cycles. I'm working on a fix.

markw65 added 3 commits April 24, 2023 19:42
Fixes microsoft#180578

The problem was that after a call to _executeTask there is a delay (at least
until the next tick, but potentially considerably longer) before the task is
added to _activeTasks. During this period a second instance of the task could
be kicked off, causing problems.

This commit fixes the issue by adding an entry (without a terminal) to
_activeTasks before returning from _executeTask. For the most part though, it
preserves the existing behavior - so for example `getActiveTasks` only returns
the tasks that have terminals attached, just as it did before; and there is no
change to when `TaskEvent` events are fired for the task.
The way dependency cycles were detected no longer works now that activeTasks is populated early. Fix it.
@markw65 markw65 force-pushed the synchronous-execute-task branch from 8b4ca12 to fda6f9c Compare April 25, 2023 02:43
@markw65
Copy link
Contributor Author

markw65 commented Apr 25, 2023

Sorry for the churn. This version fixes both issues:

  • it won't report false dependencies when multiple tasks depend on a single subtask (A depends on B and C, and B and C each depend on D)
  • it will report actual cycles (A depends on B depends on A).

@meganrogge
Copy link
Contributor

Thanks so much for looking into this. We have endgame this week, so i'll test your PRs after that.

Fixes microsoft#180850

If a task appears multiple times within a dependency hierarchy, it will usually
still be running for each subsequent visit, and won't be launched again. But if
it finishes before the next instance is visited, it will be run a second time.
This can easily happen if a parent task's `dependsOrder` is set to
`"sequence"`. This fixes the problem by keeping track of the promises
associated with all the tasks that have been started so far.
@markw65
Copy link
Contributor Author

markw65 commented Apr 25, 2023

I just added a commit that fixes #180850 - but I can't see a way to link this pull request to that issue...

@meganrogge
Copy link
Contributor

This works well, thank you!

meganrogge
meganrogge previously approved these changes May 1, 2023
@meganrogge meganrogge requested a review from alexr00 May 1, 2023 20:26
@meganrogge meganrogge force-pushed the synchronous-execute-task branch from 4cfa4f8 to 08929a2 Compare May 2, 2023 12:39
@meganrogge meganrogge force-pushed the synchronous-execute-task branch from da9676e to ff7d86a Compare May 2, 2023 12:50
@meganrogge meganrogge requested a review from Tyriar May 2, 2023 13:00
Copy link
Contributor Author

@markw65 markw65 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change breaks 180850. I'm not allowed to request changes apparently...

@markw65
Copy link
Contributor Author

markw65 commented May 2, 2023

@meganrogge you marked this as fixing #180243 - but it doesn't (it doesn't even address it). It does fix #180850 though.

@meganrogge
Copy link
Contributor

@@ -294,7 +294,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
const lastTaskInstance = this.getLastInstance(task);
const terminalData = lastTaskInstance ? this._activeTasks[lastTaskInstance.getMapKey()] : undefined;
if (terminalData && terminalData.promise && !validInstance) {
if (terminalData && terminalData.promise && !validInstance && !task.configurationProperties.dependsOn?.length) {
Copy link
Contributor Author

@markw65 markw65 May 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong? Now a task with dependencies can be run as many times as you like, regardless of its instanceLimit.

There is another bug here, which is that a task with an instance limit can be launched as a dependent task without adding to its instance count, and then launched as a top level task, possibly exceeding its intstancelimit. The fix for that is to move the instance limit checking into _executeTask; and I started a diff to do that. But I wanted to let this settle first...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now a task with dependencies can be run as many times as you like, regardless of its instanceLimit.

When I tested, that was not the case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably because the ones that are already in encounteredTasks are returned here and not rerun https://github.com/markw65/vscode/blob/0476301a02badbe7d3a4ba2aa96599c66960e4d6/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts#L552

Copy link
Contributor Author

@markw65 markw65 May 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it does. Try this with a current build:

{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "shell",
      "command": "echo dependent",
      "label": "dependent",
      "problemMatcher": []
    },
    {
      "type": "shell",
      "command": "echo Running top level task...;sleep 5;echo done",
      "label": "toplevel",
      "dependsOn": ["dependent"],
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "problemMatcher": []
    }
  ]
}

Now hit Cmd-Shift-B a few times; each one will start a new task, rather than telling you the task is already running. It was already somewhat broken (#180541) before this commit; but without this specific change, this commit would have fixed #180541. Now its completely broken it...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, sorry, I didn't get notified about your response until this morning for some reason...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markw65 In insider's, with these changes and that task, I cannot reproduce the issue you describe. I see a notification that the task is already active and am asked if i'd like to terminate or restart it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meganrogge Are you sure its in the insiders you're testing with? In order to get that popup, run has to return { kind: TaskExecuteKind.Active, ... }, and after the change above, it can only do that if the task has no dependents. Tasks with dependents simply ignore their instanceLimit.

If I checkout d980921 (main after this was merged), and build, the problem above repros. If I remove the && !task.configurationProperties.dependsOn?.length the problem goes away; and if I checkout main as of now, the problem has been fixed by #180546.

@markw65
Copy link
Contributor Author

markw65 commented May 2, 2023

@markw65 it does fix that with this change

Ah - I missed that.

Copy link
Member

@Tyriar Tyriar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meganrogge I'm not totally clear what's going on in this change, shall we talk through it tomorrow?

@meganrogge meganrogge requested a review from Tyriar May 4, 2023 01:29
@@ -294,7 +294,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
const lastTaskInstance = this.getLastInstance(task);
const terminalData = lastTaskInstance ? this._activeTasks[lastTaskInstance.getMapKey()] : undefined;
if (terminalData && terminalData.promise && !validInstance) {
if (terminalData && terminalData.promise && !validInstance && !task.configurationProperties.dependsOn?.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now a task with dependencies can be run as many times as you like, regardless of its instanceLimit.

When I tested, that was not the case.

@@ -294,7 +294,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {
}
const lastTaskInstance = this.getLastInstance(task);
const terminalData = lastTaskInstance ? this._activeTasks[lastTaskInstance.getMapKey()] : undefined;
if (terminalData && terminalData.promise && !validInstance) {
if (terminalData && terminalData.promise && !validInstance && !task.configurationProperties.dependsOn?.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably because the ones that are already in encounteredTasks are returned here and not rerun https://github.com/markw65/vscode/blob/0476301a02badbe7d3a4ba2aa96599c66960e4d6/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts#L552

@meganrogge meganrogge merged commit d980921 into microsoft:main May 4, 2023
@markw65 markw65 deleted the synchronous-execute-task branch May 10, 2023 21:41
@github-actions github-actions bot locked and limited conversation to collaborators Jun 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

There is a race starting dependent tasks
4 participants