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

Introduce proper custom execution lifecycle #9559

Closed

Conversation

tsmaeder
Copy link
Contributor

@tsmaeder tsmaeder commented Jun 7, 2021

What it does

Fixes "Review CustomExecution Lifecycle" #9418 by introducing a proper lifecycle for custom execution objects. The approach is detailed in the linked issue.

How to test

I've tested with the extensions linked off #8767 and #9366 as well as the npm tasks from the typescript built-in.
I was executing tasks an observing the back end log to make sure custom executions were being created and disposed of.

Review checklist

Reminder for reviewers

@tsmaeder tsmaeder mentioned this pull request Jun 7, 2021
29 tasks
@colin-grant-work colin-grant-work added the tasks issues related to the task system label Jun 7, 2021
@colin-grant-work colin-grant-work changed the title Introduce proper custom exceution lifecycle Introduce proper custom execution lifecycle Jun 7, 2021
Copy link
Contributor

@colin-grant-work colin-grant-work left a comment

Choose a reason for hiding this comment

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

The comments below are only superficial, as I'm not very familiar with how tasks are passed into the plugin system by plugins. It appears that they come as functions to execute when the task is activated, and in the past (when #8767 was filed) we had no mechanism for handling those, and after creating such a mechanism, we failed to handle the creation and deletion of those objects correctly (--> #9366)? I see some discussion of VSCode's approach in #9416 (very helpful, thanks!), but I don't have any intuition about the occasions Theia accesses these objects or what expectations plugins might have about their durability. Beyond the issues addressed, I wonder if you might be willing to explain the functionality you mean to provide and the problems you mean to solve / avoid?

packages/plugin-ext/src/main/browser/tasks-main.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/plugin/tasks/tasks.ts Show resolved Hide resolved
Comment on lines 77 to 78
const result = this.providedCustomExecutions.get(id);
return result ? result : this.oneOffCustomExecutions.get(id);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this different from

return this.providedCustomExecutions.get(id) ?? this.oneOffCustomExecutions.get(id)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Typescript keeps keeping ahead of me.

Comment on lines 201 to 217
return Promise.resolve(provider.provideTasks(token)).then(tasks => {
if (tasks) {
for (const task of tasks) {
if (task.taskType === 'customExecution') {
task.executionId = this.addCustomExecution(task.callback);
task.callback = undefined;
return tasks.map(task => {
const dto = converter.fromTask(task);
if (dto && CustomExecution.is(task.execution!)) {
dto.executionId = this.addProvidedCustomExecution(task.execution);
addedExecutions++;
}
}
return dto;
}).filter(task => !!task).map(task => task!);
} else {
return undefined;
}
}).then(tasks => {
console.info(`provideTasks: added ${addedExecutions} executions`);
return tasks;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be clearer as an async function rather than chained promises.
Same immediately below, though that one's less complex.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

t.b.h, I'm not a huge fan of async/await in particular when the control flow is complicated. A simple reason for it is when we get stack traces from the compiled javascript and it's a PITA to map it back to the original source. But it also sometimes obscures the true control flow. YMMV, of course.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree about the stack traces, most definitely. __awaiter__ __push__ __generator__ is not a very helpful stack at all. I think some of that noise should go away with the move to transpile to ES6, since at least some of it is TypeScript paraphrasing promises, though I'm not 100% certain how the traces will look later. I won't insist on async, but I will present this evidence:

Google for "callback hell": 95,500 results
Google for "async await hell": 3,690 results

:-)

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for async-await - it's more readable for me :-)

}
}
return dto;
}).filter(task => !!task).map(task => task!);
Copy link
Contributor

Choose a reason for hiding this comment

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

You can avoid the second .map here with something like this to help TS understand what you mean:

.filter((task): task is TaskDto => !!task)

packages/plugin-ext/src/plugin/type-converters.ts Outdated Show resolved Hide resolved
packages/task/src/browser/provided-task-configurations.ts Outdated Show resolved Hide resolved
@tsmaeder
Copy link
Contributor Author

@vince-fugnitto @RomanNikitenko: @colin-grant-work is doing the best he can, but I'll need a review from someone who has good knowledge of the task system on this one.

@tsmaeder
Copy link
Contributor Author

tsmaeder commented Jul 7, 2021

@RomanNikitenko @vince-fugnitto can I haz review pleeze?

@ericwill ericwill mentioned this pull request Jul 7, 2021
22 tasks
@vince-fugnitto
Copy link
Member

@tsmaeder I believe there are conflicts and some comments from Colin which are not yet addressed.
I'll be happy to review afterwards, even with my limited knowledge of the specifics of the task system :)

@tsmaeder tsmaeder force-pushed the 9418_custom_execution_lifecycle branch from 2300dd9 to 11e2183 Compare July 14, 2021 11:46
@tsmaeder
Copy link
Contributor Author

@vince-fugnitto

Copy link
Member

@vince-fugnitto vince-fugnitto left a comment

Choose a reason for hiding this comment

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

I'm currently in the process of testing if the changes work correctly for different tasks and plugins 👍

private currentToken: number = 0;
private nextToken = 1;

startUserAction(): number {
return this.nextToken++;
get onStartUserInteraction(): Event<TaskStartUserInteractionEvent> {
Copy link
Member

Choose a reason for hiding this comment

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

minor: should we rename given the guidelines: https://github.com/eclipse-theia/theia/wiki/Coding-Guidelines#event_names?

Suggested change
get onStartUserInteraction(): Event<TaskStartUserInteractionEvent> {
get onDidStartUserInteraction(): Event<TaskStartUserInteractionEvent> {

Copy link
Member

@vince-fugnitto vince-fugnitto left a comment

Choose a reason for hiding this comment

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

I confirmed that tasks from #9366 works successfully 👍 As for gradle, the tests cannot run due to the following error which seems unrelated?

image

@colin-grant-work
Copy link
Contributor

I confirmed that tasks from #9366 works successfully 👍 As for gradle, the tests cannot run due to the following error which seems unrelated?

image

@kenneth-marut-work, you've been looking at CWD issues in tasks lately. Any idea what might be going on here?

@tsmaeder
Copy link
Contributor Author

@vince-fugnitto do we have an issue for the cwd failures? Do you get a stack trace somewhere?

@vince-fugnitto
Copy link
Member

@vince-fugnitto do we have an issue for the cwd failures? Do you get a stack trace somewhere?

Unfortunately there is no stack trace, but the issue is also present on master which makes it difficult to determine if custom execution actually work:

root INFO [hosted-plugin: 30220] resolveTask: added custom execution
task INFO Created new custom task, id: 0, context: file:///home/vince/Desktop/core-vocabularies
root INFO [hosted-plugin: 30220] running custom execution with id 2b3387de-c077-4b30-8cfc-2eb0053fd37e1
task ERROR Error launching task 'assemble': Cannot read property 'cwd' of undefined

@kenneth-marut-work
Copy link
Contributor

I confirmed that tasks from #9366 works successfully 👍 As for gradle, the tests cannot run due to the following error which seems unrelated?
image

@kenneth-marut-work, you've been looking at CWD issues in tasks lately. Any idea what might be going on here?

Definitely looks familiar, but I'll have to refresh my memory, but I'll take a look later today to see if I can trace down anything

@kenneth-marut-work
Copy link
Contributor

const lastCwd = new URI(taskConfig?.options.cwd);

I had added this line in a recent PR #9695 which looks suspicious. Potential fix would be

const lastCwd = new URI(taskConfig?.options?.cwd);

@tsmaeder
Copy link
Contributor Author

@kenneth-marut-work I've filed #9743.

@svor svor mentioned this pull request Jul 22, 2021
34 tasks
@tsmaeder tsmaeder force-pushed the 9418_custom_execution_lifecycle branch from c70655b to f8d571f Compare July 27, 2021 13:32
@RomanNikitenko
Copy link
Contributor

@tsmaeder
I have a question about maps for executions:
what's relationship between executions vs providedCustomExecutions and oneOffCustomExecutions?
For example, I see that values from executions map are returned when theia.tasks.taskExecutions or vscode.tasks.taskExecutions API is called. So, I guess, executions map should contain all types of executions. Is it correct? If so - why do we need providedCustomExecutions?

@RomanNikitenko
Copy link
Contributor

Use case:

  • run a long task with CustomExecution
  • refresh a page
  • the task is still running
  • I guess providedCustomExecutions and oneOffCustomExecutions maps are empty after refreshing a page

what's the true way to get execution for the task?

this.proxy.$terminateTask(execution.id);
}
};
if (execution.task.executionId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that you use executionId field to check if it's CustomExecution task.
So - if only tasks with CustomExecution have this field - should we provide some clear/more readable way for that...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you have in mind? I'm not trying to do any typing here.

Copy link
Contributor

@RomanNikitenko RomanNikitenko Aug 5, 2021

Choose a reason for hiding this comment

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

only tasks with CustomExecution have this field

is it true?

Copy link
Contributor

@RomanNikitenko RomanNikitenko Sep 27, 2021

Choose a reason for hiding this comment

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

As far as I know a task of any type (not only customExecution) can have execution - the object is created when a task is started ($onDidStartTask() => getTaskExecution()) and is stored to the map with executions using executionId.

I see that:

  • within one of your previous PRs executionId field was added to the TaskDto interface
  • within current PR you are using the field as a marker of a task with customExecution
    first, you create the field, like:
if (dto && CustomExecution.is(task.execution!)) {
    dto.executionId = this.addProvidedCustomExecution(task.execution);

and in the line 281 above you check - if the field exists - then result.task.execution = this.getCustomExecution(execution.task.executionId);)

So, in my comment above I meant that for me it's not obvious that executionId field of the TaskDto interface is a marker for customExecution tasks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for me it's not obvious that executionId field of the TaskDto interface is a marker for customExecution tasks.

If you think the executionId field is named confusingly, I'm all up for renaming it to something more clear. But since it wasn't introduced in this PR, it's not a concern for this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

  • it's not a concern for this PR

sorry, but for me it looks like within current PR you are trying to use common for all tasks field as a marker for customExecution task. My first comment here was about if (execution.task.executionId).

If I'm not mistaken, this field was introduced within one of your previous PR. If so, it's important to know what you think about it, what intention was...

@tsmaeder
Copy link
Contributor Author

tsmaeder commented Aug 5, 2021

@tsmaeder
I have a question about maps for executions:
what's relationship between executions vs providedCustomExecutions and oneOffCustomExecutions?
For example, I see that values from executions map are returned when theia.tasks.taskExecutions or vscode.tasks.taskExecutions API is called. So, I guess, executions map should contain all types of executions. Is it correct? If so - why do we need providedCustomExecutions?

"executions" just keep track of the running tasks, whereas the other maps hold on to custom executions that clients have provided. The terminology is confusing, but that comes from the use of those terms in the vscode API itself 🤷. "TaskExecution" vs. "CustomExecution".

@RomanNikitenko
Copy link
Contributor

@tsmaeder
could you share your opinion about #9559 (comment)

@tsmaeder
Copy link
Contributor Author

tsmaeder commented Aug 9, 2021

Use case:

* run a long task with `CustomExecution`

* refresh a page

* the task is still running

* I guess `providedCustomExecutions` and `oneOffCustomExecutions` maps are empty after refreshing a page

what's the true way to get execution for the task?

That really does not matter: we are holding on to the custom execution objects for when the front end asks us to execute a particular task. When we reload the front end, all tasks will be recreated, and existing custom execution objects are no longer needed.

The whole confusion arises because "task execution" and "custom execution" are totally different things. "Task execution" is the representation of a running task. A "custom execution" is really just a callback that will be invoked when running a certain task.

@tsmaeder
Copy link
Contributor Author

tsmaeder commented Aug 9, 2021

@vince-fugnitto @RomanNikitenko is on vacation now. It's been > 2 months.

@vince-fugnitto
Copy link
Member

@vince-fugnitto @RomanNikitenko is on vacation now. It's been > 2 months.

@tsmaeder it's not an area I am particularly knowledgeable about but I'll give it a try.

@svor svor mentioned this pull request Aug 12, 2021
30 tasks
@tsmaeder
Copy link
Contributor Author

tsmaeder commented Sep 6, 2021

@RomanNikitenko ping

@RomanNikitenko
Copy link
Contributor

@RomanNikitenko ping

I'm going to revisit the PR in the near few days...

@svor svor mentioned this pull request Sep 7, 2021
26 tasks
@RomanNikitenko
Copy link
Contributor

RomanNikitenko commented Sep 28, 2021

@tsmaeder
Hello, Thomas, sorry for the delay...

To be honest, I don't have an experience related to customExecution.
I explored the changes and your idea here - it looks good to me!

At the same time I'm still not sure about usage of executionId field, commented above.

Could you rebase your branch - I could test your changes, although, looking at the changes, I don't expect any regressions...
Thanks in advance!

@svor svor mentioned this pull request Sep 29, 2021
25 tasks
tsmaeder and others added 4 commits October 4, 2021 11:45
Signed-off-by: Thomas Mäder <[email protected]>

Co-authored-by: colin-grant-work <[email protected]>
Signed-off-by: Thomas Mäder <[email protected]>
@tsmaeder tsmaeder force-pushed the 9418_custom_execution_lifecycle branch from f8d571f to 606725c Compare October 4, 2021 09:53
@svor svor mentioned this pull request Oct 19, 2021
28 tasks
@svor svor mentioned this pull request Nov 9, 2021
18 tasks
@svor svor mentioned this pull request Nov 30, 2021
19 tasks
@JonasHelming JonasHelming added the decision_needed issues that require decisions on ways forward (ex: if they should be updated or closed) label Mar 4, 2022
@tsmaeder
Copy link
Contributor Author

tsmaeder commented Mar 7, 2022

I still think this is a good PR. What's missing is someone to review it: for obvious reasons, we can't expect @RomanNikitenko to do it any time soon.

@tsmaeder tsmaeder removed the decision_needed issues that require decisions on ways forward (ex: if they should be updated or closed) label Mar 29, 2022
@tsmaeder
Copy link
Contributor Author

Not likely to get merged anytime soon.

@tsmaeder tsmaeder closed this May 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tasks issues related to the task system
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

6 participants