-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Release JS Blob after BrowserFileStream is disposed. Fixes #35540 #37004
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
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does
Preservedo here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use this pattern in a lot of places across ASP.NET Core and Blazor to avoid the analyzer warning https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2012 when doing fire-and-forget on a
ValueTask. Unfortunately the explanation given in the CA2012 docs only says:... but doesn't give deeper justification or concrete examples of why fire-and-forget may degrade performance. Knowing the internals of JS interop I don't see any obvious reason why it would be problematic for perf to fire-and-forget a disposeasync call, which we need to do and have a good justification for in this case (see comment in this code).
TBH I'm not really convinced that calling
.Preserve()is the right thing to do instead of actually honestly suppressing the warning for real. @pranavkm I think I copied the pattern from you - do you know if.Preserve()actually avoids some real problem, or is this more a case of "Tell me you're suppressing CA2012 without telling me you're suppressing CA2012"?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I think I know the reason why it could be a perf concern.
ValueTaskinstances that wrap values from a reusable pool of task source instances can't return them to the pool until yourawaitis done. So not awaiting could be leaking.In this case, there's no
IValueTaskSourceor pooling so the concern is not applicable.I suppose
Preservemight release the original pooled object back to the pool and create a new one that external code can hold onto as long as it likes, so would genuinely be a valid way of not having the issue that CA2012 complains about (even though it's not applicable in our case anyway). But I don't really know for sure if my interpretation is correct, because thePreservedocs don't say anything about the implementation, and the sources are pretty nontrivial to reason about.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway thanks for raising this question @TanayParikh - you forced me to learn more about this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stephentoub was an advocate for that analyzer (and enabled it in our repo). Perhaps some feedback for the description of the analyzer
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A
ValueTask<T>can wrap aT, aTask<T>, or anIValueTaskSource<T>. Given an arbitraryValueTask<T>(e.g. a function returns one to you), you should "consume" it once and only once, where "consume" here means awaiting it, calling.Result, calling.GetAwaiter().GetResult(), calling.AsTask(), etc.: basically you need to assume that the moment you've retrieved its result, it's no longer yours, because it may have been wrapping a pooled resource in the form of anIValueTaskSource<T>, and that implementation used itsGetResult()implementation as a signal that you're now done with it and the object can be reused. https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/ provides a lot more details. The flip side of that, as is surmised earlier in the comment chain, is that if you get such anIValueTaskSource<T>-backedValueTask<T>and never await it or otherwise callGetResult(), you're not sending that signal to the implementation, so for example if it's pooled, you just lost that object from the pool.Preserve()returns a newValueTask<T>that is identical if it's backed by aTorTask<T>, but is the equivalent of doingnew ValueTask<T>(oldVT.AsTask())if it was backed by anIValueTaskSource<T>, and thatAsTask()will properly register with theIValueTaskSource<T>to know when it's completed and callGetResultwhen it is. As a result,Preserve()can be used in a situation where a) you want fire-and-forget and might be dealing with a pooled resource, or b) you want to be able to consume theValueTask<T>multiple times... both are advanced.