-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Introduce proper custom execution lifecycle #9559
Conversation
There was a problem hiding this 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?
const result = this.providedCustomExecutions.get(id); | ||
return result ? result : this.oneOffCustomExecutions.get(id); |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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.
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; | ||
}); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
:-)
There was a problem hiding this comment.
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!); |
There was a problem hiding this comment.
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)
@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. |
@RomanNikitenko @vince-fugnitto can I haz review pleeze? |
@tsmaeder I believe there are conflicts and some comments from Colin which are not yet addressed. |
2300dd9
to
11e2183
Compare
There was a problem hiding this 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> { |
There was a problem hiding this comment.
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?
get onStartUserInteraction(): Event<TaskStartUserInteractionEvent> { | |
get onDidStartUserInteraction(): Event<TaskStartUserInteractionEvent> { |
There was a problem hiding this 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?
@kenneth-marut-work, you've been looking at CWD issues in tasks lately. Any idea what might be going on here? |
@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:
|
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 |
I had added this line in a recent PR #9695 which looks suspicious. Potential fix would be
|
@kenneth-marut-work I've filed #9743. |
c70655b
to
f8d571f
Compare
@tsmaeder |
Use case:
what's the true way to get |
this.proxy.$terminateTask(execution.id); | ||
} | ||
}; | ||
if (execution.task.executionId) { |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 theTaskDto
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
.
- it would be nice to clarify Introduce proper custom execution lifecycle #9559 (comment) first
-
If you think the executionId field is named confusingly
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...
"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". |
@tsmaeder |
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. |
@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. |
@RomanNikitenko ping |
I'm going to revisit the PR in the near few days... |
@tsmaeder To be honest, I don't have an experience related to At the same time I'm still not sure about usage of Could you rebase your branch - I could test your changes, although, looking at the changes, I don't expect any regressions... |
Signed-off-by: Thomas Mäder <[email protected]>
Signed-off-by: Thomas Mäder <[email protected]> Co-authored-by: colin-grant-work <[email protected]>
Signed-off-by: Thomas Mäder <[email protected]>
Signed-off-by: Thomas Mäder <[email protected]>
f8d571f
to
606725c
Compare
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. |
Not likely to get merged anytime soon. |
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