Skip to content

Added an additional yield point prior to possibly long running initialization #115688

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

Merged
merged 10 commits into from
May 20, 2025

Conversation

quixoticaxis
Copy link
Contributor

@quixoticaxis quixoticaxis commented May 17, 2025

Closes #115301.

The changes are:

  • introduction of an additional yield point
  • changes to the tests that depend on the current synchronous nature of the initialization
  • removal of the test that depends both on the synchronous nature of the initialization in the SendAsync call and on the order of initialization and validation.

Important note:
The setters of the class throw InvalidOperationException if the send operation is started on the instance of the class. This check is currently implemented based on whether inner _handler is null or not. Considering the current code assumes the possibility of multi-threaded initialization, this check is not super reliable.

I've updated the test, which checks this behavior, to adjust to the new order of validation. If stronger guarantees should be provided, I can think about it, but it currently feels like everything (except for locking or CompareExchange-ing additional flags around) would be only a best-effort attempt.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label May 17, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@MihaZupan
Copy link
Member

Considering the current code assumes the possibility of multi-threaded initialization, this check is not super reliable.

That's okay. This is already the case and something that SetupHandlerChain is already handling by ensuring only 1 handler ends up being used, even if more than 1 is allocated in a race.

It might still be worth adding the mitigation discussed in #115301 (comment) and similar, just in case we do hit the worst case of the init logic blocking for multiple seconds.

Also added an explanation comment
between synchronous and asynchronous calls
@quixoticaxis

This comment was marked as resolved.

Copy link
Member

@MihaZupan MihaZupan left a comment

Choose a reason for hiding this comment

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

Thanks

@MihaZupan MihaZupan added this to the 10.0.0 milestone May 18, 2025
@MihaZupan
Copy link
Member

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MihaZupan MihaZupan merged commit de76a8c into dotnet:main May 20, 2025
82 of 93 checks passed
// The setup procedure is enqueued to thread pool to prevent the caller from blocking.
async Task<HttpResponseMessage> CreateHandlerAndSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_handlerChainSetupTask ??= Task.Run(SetupHandlerChain);
Copy link
Contributor

@pentp pentp May 20, 2025

Choose a reason for hiding this comment

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

If you want to optimize this a bit more and also remove the chance of double-setup you could use new Task(s => SetupHandlerChain(s), this) + CompareExchange + Task.Start (conditionally) and you don't need the method result here (so a void Task is sufficient), can use _handler after the setup task finishes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable.
Already merged though. Do you think it justifies a separate issue? I'm not certain, because repeated initialization has been deemed not important.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it's worth the extra logic.

@quixoticaxis quixoticaxis deleted the 115301-add-yield-point branch May 20, 2025 20:21
SimaTian pushed a commit that referenced this pull request May 27, 2025
…lization (#115688)

* Added an additional yield point

* Enqueued setup logic to thread pool

Also added an explanation comment

* Changed validation order to reduce discrepancies

between synchronous and asynchronous calls

* Updated comment

Co-authored-by: Miha Zupan <[email protected]>

* Removed Volatile.Read to make the code more readable

Co-authored-by: Miha Zupan <[email protected]>

* Removed the test that is no longer viable

* Moved the nested method

to adhere to the code style

---------

Co-authored-by: Miha Zupan <[email protected]>
@github-actions github-actions bot locked and limited conversation to collaborators Jun 20, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Http community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

HttpClient async methods are blocking the first time
5 participants