Skip to content

migrate PickBestRid#54489

Open
AlesProkop wants to merge 2 commits into
dotnet:mainfrom
AlesProkop:migrate-pick-best-rid
Open

migrate PickBestRid#54489
AlesProkop wants to merge 2 commits into
dotnet:mainfrom
AlesProkop:migrate-pick-best-rid

Conversation

@AlesProkop
Copy link
Copy Markdown
Member

Summary

Migrates PickBestRid to MSBuild's multithreaded task execution model by opting into IMultiThreadableTask and routing path resolution through TaskEnvironment.

Changes

PickBestRid.cs

  • Added [MSBuildMultiThreadableTask] and implemented IMultiThreadableTask with TaskEnvironment = TaskEnvironment.Fallback.
  • Resolved RuntimeGraphPath via TaskEnvironment.GetAbsolutePath(...) before File.Exists and RuntimeGraphCache.GetRuntimeGraph.
  • Kept the original RuntimeGraphPath in error messages (no absolutized leak).

GivenAPickBestRidMultiThreading.cs (new) — 4 correctness tests:

Test Guards against
ItResolvesRelativeRuntimeGraphPathAgainstProjectDirectory Path resolution falling back to process CWD.
MatchingRidOutputContainsOnlyRidNoPathPrefix Absolutized path leaking into the MatchingRid output.
MissingFileErrorContainsOriginalRelativePathNotAbsolutized Error-message path inflation.
ItProducesIdenticalOutcomeInFallbackAndIsolatedModes_Success Behavior parity between Fallback and isolated TaskEnvironment.

Validation

  • MSBuild Thread-Safe Task Analyzer: 0 diagnostics on PickBestRid.cs.
  • New tests: 4/4 pass. Existing GivenAPickBestRid tests: 6/6 still pass.

@AlesProkop AlesProkop marked this pull request as ready for review June 4, 2026 11:01
Copilot AI review requested due to automatic review settings June 4, 2026 11:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the PickBestRid MSBuild task to MSBuild’s multi-threaded task execution model by implementing IMultiThreadableTask and routing file path resolution through TaskEnvironment, with tests intended to validate behavior in isolated vs fallback execution modes.

Changes:

  • Updated PickBestRid to opt into multi-threaded execution and resolve RuntimeGraphPath via TaskEnvironment.GetAbsolutePath(...) before file access and runtime graph loading.
  • Added a new unit test file to validate relative-path resolution and error-message behavior (including avoiding absolutized path leaks).
  • Added a coarse-grained lock in RuntimeGraphCache around task-object caching of runtime graphs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
test/Microsoft.NET.Build.Tasks.Tests/GivenAPickBestRidMultiThreading.cs Adds tests around TaskEnvironment-based path resolution and error messaging for PickBestRid.
src/Tasks/Microsoft.NET.Build.Tasks/RuntimeGraphCache.cs Adds locking around runtime graph caching via BuildEngine registered task objects.
src/Tasks/Microsoft.NET.Build.Tasks/PickBestRid.cs Implements IMultiThreadableTask, uses TaskEnvironment to resolve runtime graph path prior to file operations and caching.

Comment on lines +8 to +12
public class GivenAPickBestRidMultiThreading
{
private const string RuntimeGraphContent = @"{
""runtimes"": {
""any"": { ""#import"": [""base""] },
Comment on lines 35 to 53
string key = GetTaskObjectKey(runtimeJsonPath);

RuntimeGraph result;
object existingRuntimeGraphTaskObject = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.AppDomain);
if (existingRuntimeGraphTaskObject == null)
lock (s_cacheLock)
{
result = JsonRuntimeFormat.ReadRuntimeGraph(runtimeJsonPath);

_buildEngine.RegisterTaskObject(key, result, RegisteredTaskObjectLifetime.AppDomain, true);
RuntimeGraph result;
object existingRuntimeGraphTaskObject = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.AppDomain);
if (existingRuntimeGraphTaskObject == null)
{
result = JsonRuntimeFormat.ReadRuntimeGraph(runtimeJsonPath);

_buildEngine.RegisterTaskObject(key, result, RegisteredTaskObjectLifetime.AppDomain, true);
}
else
{
result = (RuntimeGraph)existingRuntimeGraphTaskObject;
}

return result;
}
Copy link
Copy Markdown
Contributor

@OvesN OvesN left a comment

Choose a reason for hiding this comment

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

LGTM, but I would try different solution with the lock. Otherwise this migration seems fine

RuntimeGraph result;
object existingRuntimeGraphTaskObject = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.AppDomain);
if (existingRuntimeGraphTaskObject == null)
lock (s_cacheLock)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Locking here is the right think to do I think, but I think we can do something more granular than one global lock. We only really care about two threads colliding on the same taskObjectKey - so per-key locks would probably be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants