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

[Bug]: Please re-initilize "_terminatedTool" in class Microsoft.Build.Utilities.ToolTask #8541

Closed
gpwen opened this issue Mar 7, 2023 · 6 comments · Fixed by #8544
Closed
Assignees
Labels

Comments

@gpwen
Copy link
Contributor

gpwen commented Mar 7, 2023

Issue Description

In the following code:
https://github.com/dotnet/msbuild/blob/main/src/Utilities/ToolTask.cs

The private field _terminatedTool has not be initialized in method ExecuteTool(). As a result, in rare cases where:

  1. ToolTask.Execute() being called multiple times from derived classes, and;
  2. The tool execution being timed out once.

Such timed-out state will be incorrectly permanent, impacting all following ToolTask.Execute() calls, and forcing the method to return false even if the execution succeeded.

The problem can be easily addressed by reinitializing _terminatedTool field to false in method ExecuteTool(), along with other private fields.

Thanks!

Steps to Reproduce

  1. Create a CLI tool that would wait the amount of time (ms) specified on command line;
  2. Create a ToolTask derived class to execute that tool;
  3. Override Execute(), calling base.Execute() multiple times to execute the tool multiple times;
  4. In the first execution, set delay to a larger value (>100s), and set it to smaller value in all subsequent execution (< 1s);
  5. Set Timeout of the task to 5s, so the first call will timed out, and subsequent ones won't;
  6. Watch how all subsequent base.Execute() returns false, so the task never succeeds.

Expected Behavior

The first execution timed out (and fail), but subsequent execution should succeed.

Actual Behavior

After the first execution timed out, all subsequent execution failed even if they actually succeeded.

Analysis

Private field _terminatedTool has not been reinitialized correctly in ToolTask.ExecuteTool() method.

Versions & Configurations

The latest version (17.5).

@gpwen gpwen added bug needs-triage Have yet to determine what bucket this goes in. labels Mar 7, 2023
@gpwen
Copy link
Contributor Author

gpwen commented Mar 7, 2023

The problem found in MSBuild version 17.4.1.60106, but it's the same in the latest version.

@rainersigwald
Copy link
Member

I think that would be a reasonable change, though ToolTask is not particularly oriented to running multiple tools. @gpwen would you be interested in submitting a PR?

@gpwen
Copy link
Contributor Author

gpwen commented Mar 7, 2023

Thanks for considering the change. I'll submit a PR.

Some background: I need to execute an external command repeatedly (predefined number of maximum repeats) with timeout until it succeeds. Not sure if I can execute another MSBuild task from within a task, which would look better. I called base.Execute() multiple times as a workaround. I know it's probably not a good idea after read through the code. Really appreciate if you can suggest an alternative.

gpwen pushed a commit to gpwen/msbuild that referenced this issue Mar 8, 2023
ToolTask uses a private flag "_terminatedTool" to indicate the task
execution timed out or cancelled. That flag should be reset on each
execution, otherwise the return value of all following executions
could be changed.

Fixes dotnet#8541
gpwen pushed a commit to gpwen/msbuild that referenced this issue Mar 8, 2023
@gpwen
Copy link
Contributor Author

gpwen commented Mar 8, 2023

I don't know there are Linux/Mac tests. Sorry for the unit test break in the first commit. I'm more than happy to make those work. The second commit should do the trick.

@rainersigwald
Copy link
Member

Not sure if I can execute another MSBuild task from within a task

It is possible to do this, by manually calling the constructor and property accessors of the task, including setting BuildEngine

/// <summary>
/// This property is set by the build engine to allow a task to call back into it.
/// </summary>
/// <value>The interface on the build engine available to tasks.</value>
IBuildEngine BuildEngine

and then calling Execute(). However, I don't generally recommend it and it doesn't seem obviously better than your current retrying-ToolTask approach.

@gpwen
Copy link
Contributor Author

gpwen commented Mar 8, 2023

Thanks for the information! It's good to know I can do that as well.

@AR-May AR-May removed the needs-triage Have yet to determine what bucket this goes in. label Mar 14, 2023
@AR-May AR-May added Priority:3 Work that is nice to have backlog labels Mar 14, 2023
JaynieBai pushed a commit that referenced this issue Apr 6, 2023
ToolTask uses a private flag _terminatedTool to indicate the task execution timed out or cancelled. That flag should be reset on each execution, otherwise the return value of all following executions could be changed.

Fixes #8541

Context
When the same ToolTask instance being executed multiple times (by derived classes, for example), the return status of all following executions might be wrong (false) if the first execution timed-out or cancelled. Such case may arise if user try to run an external tool with retry and timeout. The problem is the internal _terminatedTool flag has not been reset on each execution. Reset that flag on each execution solved the problem.

Changes Made
Resets the internal flag ToolTask._terminatedTool on each task execution.

Testing
The following unit test has been added: Microsoft.Build.UnitTests.ToolTaskThatTimeoutAndRetry. It has 3 cases that verify no repeated execution, repeated execution with or without timeout. It's the last case that has been fixed, the rest is for regression.

All ToolTask unit tests passed.

Notes
The Timeout setting in the unit test might be tricky. On slow hardware you may want to set that to a larger value;
The unit test needs PowerShell to run, only Windows platform considered.
@AR-May AR-May added the triaged label Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants