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

Split lock to prevent deadlock in Http2Stream. #47769

Merged
merged 4 commits into from
Feb 8, 2021

Conversation

ManickaP
Copy link
Member

@ManickaP ManickaP commented Feb 2, 2021

Http2Connection.ChangeInitialWindowSize locks connection's SyncObject and calls Http2Stream.OnWindowUpdate which locks stream's SyncObject.
Http2Stream.Complete is called only while stream's SyncObject lock is take and then it calls Http2Connection.RemoveStream that locks connection SyncObject.

Fixes #48220

@ghost
Copy link

ghost commented Feb 2, 2021

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

Issue Details

Http2Connection.ChangeInitialWindowSize locks connection's SyncObject and calls Http2Stream.OnWindowUpdate which locks stream's SyncObject.
Http2Stream.Complete is called only while stream's SyncObject lock is take and then it calls Http2Connection.RemoveStream that locks connection SyncObject.

Author: ManickaP
Assignees: -
Labels:

area-System.Net.Http

Milestone: -

@ManickaP ManickaP force-pushed the mapichov/http2_deadlock branch from 2e12e2c to 90d82cd Compare February 2, 2021 16:07
Http2Connection.ChangeInitialWindowSize locks connection's SyncObject and calls Http2Stream.OnWindowUpdate which locks stream's SyncObject.
Http2Stream.Complete is called only while stream's SyncObject lock is take and then it calls Http2Connection.RemoveStream that locks connection SyncObject.
@ManickaP ManickaP force-pushed the mapichov/http2_deadlock branch from 90d82cd to d68a2d4 Compare February 2, 2021 16:38
@ManickaP
Copy link
Member Author

ManickaP commented Feb 2, 2021

/azp list

@dotnet dotnet deleted a comment from azure-pipelines bot Feb 2, 2021
@ManickaP
Copy link
Member Author

ManickaP commented Feb 2, 2021

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@geoffkizer
Copy link
Contributor

All this credit logic has changed since the last time I looked at it, so my understanding may be out of date here. That said...

Is there some reason that the new lock needs to reside on Http2Stream, as opposed to being encapsulated within the credit management logic? The latter seems a lot safer, and that's how it worked originally if I recall correctly.

There are several references to _creditWaiter in SendDataAsync that do not take the new lock. Should they? They certainly look suspicious.

@geoffkizer
Copy link
Contributor

More generally --

At some point we made a change to not use CreditManager here, and try to use CreditWaiter directly. Perhaps we should revisit this?

@ManickaP
Copy link
Member Author

ManickaP commented Feb 4, 2021

Is there some reason that the new lock needs to reside on Http2Stream

At the moment it seems more suitable since we need to guard _availableCredit as well.


There are several references to _creditWaiter in SendDataAsync that do not take the new lock. Should they? They certainly look suspicious.

Could you please point out where? I just wen through all occurrences of _creditWaiter and couldn't find any except for this:

// Logically this is part of the else block above, but we can't await while holding the lock.
Debug.Assert(_creditWaiter != null);
sendSize = await _creditWaiter.AsValueTask().ConfigureAwait(false);

But that cannot be inside a lock and never was before.


At some point we made a change to not use CreditManager here, and try to use CreditWaiter directly. Perhaps we should revisit this?

It happened here: #32624. Maybe @stephentoub has more to say about the reasons for/against.

@stephentoub
Copy link
Member

Maybe @stephentoub has more to say about the reasons for/against.

I did it for the reasons outlined in that PR (#32624): it was using a type that was more complicated and provided more features than was needed while also incurring extra allocations.

If we need an object anyway in order to have a separate lock, I have no qualms with us refactoring this logic into another helper type, allocating an instance of it instead of a separate lock object and then using it for the lock as well. We shouldn't just reuse CreditManager, though, as we'll then be back to allocating a new waiter every time the stream yields.

@geoffkizer
Copy link
Contributor

Could you please point out where?

Sorry, I misread your code. Nevermind...

@geoffkizer
Copy link
Contributor

it was using a type that was more complicated and provided more features than was needed while also incurring extra allocations.

And just to be clear -- "provided more features" refers to the fact that CreditManager supports multiple waiters, whereas in the stream credit case we only ever have one waiter, right?

That makes me feel fine about not using CreditManager here, but still using it other places, since the requirements are different. I just to make sure we are not either (a) duplicating code unnecessarily or (b) missing out on using this optimization in other places, but it sounds like that's not the case here.

@stephentoub
Copy link
Member

And just to be clear -- "provided more features" refers to the fact that CreditManager supports multiple waiters, whereas in the stream credit case we only ever have one waiter, right?

Correct (and as part of supporting multiple waiters, it doesn't try to reuse any of them).

@GSPP
Copy link

GSPP commented Feb 5, 2021

The title says "OnWindowsUpdate". I opened this issue because I wanted to know how a running Windows Update could possibly affect a .NET process 😆

@ManickaP ManickaP changed the title Split lock to prevent deadlock between OnWindowsUpdate and Complete. Split lock to prevent deadlock in Http2Stream. Feb 5, 2021
@ManickaP ManickaP added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Feb 5, 2021
@ManickaP
Copy link
Member Author

ManickaP commented Feb 5, 2021

I'm working on a functional test for the deadlock since I'm able to reproduce the problem. Please don't merge on my behalf.

…andler/Http2Stream.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>
@ManickaP
Copy link
Member Author

ManickaP commented Feb 5, 2021

/backport to release/5.0

@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2021

Started backporting to release/5.0: https://github.com/dotnet/runtime/actions/runs/541208321

@ManickaP
Copy link
Member Author

ManickaP commented Feb 5, 2021

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP ManickaP force-pushed the mapichov/http2_deadlock branch from d634a8d to 0daf008 Compare February 6, 2021 08:40
@ManickaP
Copy link
Member Author

ManickaP commented Feb 6, 2021

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP ManickaP removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Feb 8, 2021
@ManickaP ManickaP merged commit 0f055ba into dotnet:master Feb 8, 2021
@ManickaP ManickaP deleted the mapichov/http2_deadlock branch February 8, 2021 13:25
@karelz karelz added this to the 6.0.0 milestone Feb 12, 2021
@ManickaP ManickaP mentioned this pull request Feb 12, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Mar 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deadlock in HTTP/2
5 participants