Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 25, 2025

Fixes #12676

Context

Follow-up to #12577. The taskhost node identification used bit-packing to combine scheduledNodeId (8 bits) and HandshakeOptions (9 bits) into a single int, limiting multithreaded builds to 256 nodes. This approach was fragile and hard to maintain.

Changes Made

  • New TaskHostNodeKey record struct in CommunicationsUtilities.cs combining HandshakeOptions and NodeId as a clean, type-safe key
  • Updated NodeProviderOutOfProcTaskHost with optimized dictionary structure:
    • _nodeContexts keyed by TaskHostNodeKey for checking if a node with given (HandshakeOptions, scheduledNodeId) exists (acquisition logic)
    • _nodeIdToNodeKey for O(1) reverse lookup during node termination
    • _nodeIdToPacketFactory and _nodeIdToPacketHandler keyed by int for O(1) packet routing performance
    • _activeNodes tracks node IDs (int) for shutdown handling
    • Added atomic _nextNodeId counter for unique communication node ID generation
  • Simplified TaskHostTask - removed GenerateTaskHostNodeId method and associated bit-packing constants
  • Improved error handling - Added VerifyThrow assertions in DisconnectFromHost and CreateNode to verify context existence and prevent duplicate nodes
// Before: bit-packed int with 256-node limit
_taskHostNodeId = GenerateTaskHostNodeId(_scheduledNodeId, _requiredContext);
// (scheduledNodeId << 9) | ((int)handshakeOptions & 0x1FF)

// After: clean record struct, no limit
_taskHostNodeKey = new TaskHostNodeKey(_requiredContext, _scheduledNodeId);

This gives the best of both worlds:

  • Maintainability: Type-safe Keys for managing node lifecycle and acquisition
  • Performance: Direct Integer lookups for the high-frequency packet routing hot path

Testing

  • Added 7 unit tests for TaskHostNodeKey (equality, hash codes, dictionary keys, large node IDs)
  • All 86 existing TaskHost tests pass

Notes

  • Removes the NODE_ID_MAX_VALUE_FOR_MULTITHREADED (255) constraint entirely
  • SendData(int, INodePacket) throws NotImplementedException as it's required by INodeProvider interface but not used for task hosts (similar to TaskHostNodeManager)
Original prompt

This section details on the original issue you should resolve

<issue_title>[multithreaded] Refactor Node IDs for taskhost</issue_title>
<issue_description>followup to #12577
Refactor src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs , HandshakeOptions to use record struct as a key to nodes to increase maintanability and remove limitation to 256 nodes.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@dotnet-policy-service
Copy link
Contributor

Hello @@copilot, I noticed that you’re changing an .swr file or any file under src/Package/MSBuild.VSSetup.. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo.

Copilot AI and others added 2 commits November 25, 2025 14:52
- Created TaskHostNodeKey record struct combining HandshakeOptions and NodeId
- Updated NodeProviderOutOfProcTaskHost to use TaskHostNodeKey as dictionary keys
- Added reverse mapping from communication nodeId to TaskHostNodeKey for packet routing
- Updated TaskHostTask to use TaskHostNodeKey instead of bit-packed int
- Removed 256-node limitation for multithreaded builds
- Added unit tests for TaskHostNodeKey

Co-authored-by: JanProvaznik <[email protected]>
…of hash

Address code review feedback: Use Interlocked.Increment for generating
unique communication node IDs instead of GetHashCode() to avoid potential
hash collisions.

Co-authored-by: JanProvaznik <[email protected]>
Copilot AI changed the title [WIP] Refactor Node IDs for taskhost to enhance maintainability Refactor Node IDs for taskhost to use record struct Nov 25, 2025
Copilot AI requested a review from JanProvaznik November 25, 2025 15:05
Copilot finished work on behalf of JanProvaznik November 25, 2025 15:05
Address code review feedback: Use int keys directly for packet routing
dictionaries (_nodeIdToPacketFactory and _nodeIdToPacketHandler) instead
of going through an intermediate TaskHostNodeKey lookup. The TaskHostNodeKey
is now only used where we need to identify nodes by their logical identity
(HandshakeOptions + scheduledNodeId) for node existence checks.

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 25, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • rcxvsblobprodcus328.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/msbuild/msbuild/artifacts/bin/bootstrap/core/dotnet dotnet test src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj --filter FullyQualifiedName~TaskHost --no-build -v n (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Reverts the simplification made in the previous commit per reviewer request.
The intermediate dictionary provides cleaner lookup from communication node IDs
to TaskHostNodeKey for packet routing.

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 25, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • rcxvsblobprodcus328.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/msbuild/msbuild/artifacts/bin/bootstrap/core/dotnet dotnet test src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj --filter FullyQualifiedName~TaskHost --no-build -v n (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Apply reviewer feedback for O(1) packet routing performance:
- Use int keys (_nodeIdToPacketFactory, _nodeIdToPacketHandler) for packet routing
- Keep _nodeContexts keyed by TaskHostNodeKey for acquisition logic
- Remove intermediate _nodeIdToNodeKey dictionary
- Use _activeNodes with int (nodeId) instead of TaskHostNodeKey

This gives type-safe Keys for managing node lifecycle while maintaining
direct Integer lookups for high-frequency packet routing.

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 25, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • rcxvsblobprodcus328.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/msbuild/msbuild/artifacts/bin/bootstrap/core/dotnet dotnet test src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj --filter FullyQualifiedName~TaskHost --no-build -v n (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of JanProvaznik November 25, 2025 17:32
Copy link
Member

@JanProvaznik JanProvaznik left a comment

Choose a reason for hiding this comment

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

@copilot address comments

/// </summary>
/// <param name="nodeId">The node to which data shall be sent.</param>
/// <param name="packet">The packet to send.</param>
public void SendData(int nodeId, INodePacket packet)
Copy link
Member

Choose a reason for hiding this comment

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

is this method needed for anything now? if no, remove

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The SendData(int nodeId, INodePacket packet) method is required by the INodeProvider interface but not actually used for task hosts (task hosts use SendData(TaskHostNodeKey, ...) via TaskHostTask). Similar to TaskHostNodeManager.SendData, keeping it as NotImplementedException to satisfy the interface contract.

Copy link
Member

@JanProvaznik JanProvaznik Nov 27, 2025

Choose a reason for hiding this comment

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

this is a bit sus, but I suppose it works, flagging this for reviewers

- Add VerifyThrow in DisconnectFromHost to verify context exists
- Restore assert in CreateNode to verify no duplicate context
- Eliminate linear search in NodeContextTerminated by adding _nodeIdToNodeKey reverse mapping
- Consolidate to single NodeContextTerminated(int nodeId) method
- Keep SendData(int, INodePacket) throwing NotImplementedException (required by interface but not used for task hosts)

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 27, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • rcxvsblobprodcus328.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/msbuild/msbuild/artifacts/bin/bootstrap/core/dotnet dotnet test src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj --filter FullyQualifiedName~TaskHost --no-build -v n (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of JanProvaznik November 27, 2025 12:43
Copilot AI requested a review from JanProvaznik November 27, 2025 12:43
@JanProvaznik JanProvaznik marked this pull request as ready for review November 27, 2025 13:13
Copilot finished reviewing on behalf of JanProvaznik November 27, 2025 13:17
Copy link
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 successfully refactors the TaskHost node identification system from fragile bit-packing (limiting builds to 256 nodes) to a clean, type-safe TaskHostNodeKey record struct. The refactoring removes the artificial node limit while improving code maintainability and preserving O(1) packet routing performance.

Key Changes

  • Introduced TaskHostNodeKey record struct combining HandshakeOptions and NodeId for type-safe node identification
  • Restructured NodeProviderOutOfProcTaskHost dictionaries to separate concerns: acquisition logic uses TaskHostNodeKey, while packet routing uses integer IDs for performance
  • Removed bit-packing constants and the GenerateTaskHostNodeId method from TaskHostTask
  • Added comprehensive unit tests validating the new key structure including large node IDs beyond the previous 256-node limit

Reviewed changes

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

File Description
src/Shared/CommunicationsUtilities.cs Defined TaskHostNodeKey record struct and removed obsolete documentation about bit-packing constraints
src/Build/Instance/TaskFactories/TaskHostTask.cs Replaced integer node ID with TaskHostNodeKey, removed bit-packing logic and related constants
src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs Refactored dictionary structure with atomic node ID generation, separate key-based and ID-based lookups for optimal performance
src/Build.UnitTests/BackEnd/TaskHostNodeKey_Tests.cs Added 7 comprehensive tests for equality, hashing, dictionary usage, and validation of unlimited node IDs
Comments suppressed due to low confidence (1)

src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs:615

  • Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.
            if (!_nodeContexts.ContainsKey(nodeKey))
            {
                nodeCreationSucceeded = CreateNode(nodeKey, factory, handler, configuration, taskHostParameters);
            }
            else
            {
                // node already exists, so "creation" automatically succeeded
                nodeCreationSucceeded = true;
            }

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

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.

[multithreaded] Refactor Node IDs for taskhost

2 participants