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

Fix ReadBufferState dispose cases #110739

Merged
merged 14 commits into from
Dec 22, 2024

Conversation

MaxPatri
Copy link
Contributor

I moved the initialization of ReadBufferState after other related operations to prevent skipping Dispose in case of an exception, and consequently, to avoid memory leaks.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Dec 16, 2024
@stephentoub
Copy link
Member

to avoid memory leaks

What leaks?

@huoyaoyuan
Copy link
Member

If ReadStack.Initialize throws, the array rented in ReadBufferState won't be returned. new JsonReaderState won't throw because it's pure POCO initialization.
There won't be leak, because failing to return pooled array isn't a fatal leak.

@eiriktsarpalis
Copy link
Member

eiriktsarpalis commented Dec 16, 2024

This is the code for ReadStack.Initialize:

internal void Initialize(JsonTypeInfo jsonTypeInfo, bool supportContinuation = false)
{
JsonSerializerOptions options = jsonTypeInfo.Options;
if (options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve)
{
ReferenceResolver = options.ReferenceHandler!.CreateResolver(writing: false);
PreserveReferences = true;
}
Current.JsonTypeInfo = jsonTypeInfo;
Current.JsonPropertyInfo = jsonTypeInfo.PropertyInfoForTypeInfo;
Current.NumberHandling = Current.JsonPropertyInfo.EffectiveNumberHandling;
Current.CanContainMetadata = PreserveReferences || jsonTypeInfo.PolymorphicTypeResolver?.UsesTypeDiscriminators == true;
SupportContinuation = supportContinuation;
}

And this is the code for the JsonReaderState:

public JsonReaderState(JsonReaderOptions options = default)
{
_lineNumber = default;
_bytePositionInLine = default;
_inObject = default;
_isNotPrimitive = default;
_valueIsEscaped = default;
_trailingCommaBeforeComment = default;
_tokenType = default;
_previousTokenType = default;
_readerOptions = options;
// Only allocate if the user reads a JSON payload beyond the depth that the _allocationFreeContainer can handle.
// This way we avoid allocations in the common, default cases, and allocate lazily.
_bitStack = default;
}

Excluding potential bugs in the underlying implementation(s), neither of these calls are expected to throw. Is there a real-world scenario that could result in unreturned buffers?

FWIW I don't think we couldn't consider this change, but to make things more explicit I would just move the initialization methods inside the try clause instead (or at least leave a comment explaining why the state object should be created last).

@eiriktsarpalis eiriktsarpalis added the needs-author-action An issue or pull request that requires more info or actions from the author. label Dec 16, 2024
@dotnet-policy-service dotnet-policy-service bot removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Dec 16, 2024
@MaxPatri
Copy link
Contributor Author

Moved ReadBufferState initialization under try clause.

@eiriktsarpalis
Copy link
Member

Moved ReadBufferState initialization under try clause.

That's not quite that I meant. My suggestion was to either keep the current order (but nest the initialization methods inside the try clause) or add a comment to your original change explaining why the ReadBufferState is being initialized last.

@MaxPatri
Copy link
Contributor Author

Initial change was returned with comment

@MaxPatri
Copy link
Contributor Author

@dotnet-policy-service agree

Copy link
Member

@eiriktsarpalis eiriktsarpalis left a comment

Choose a reason for hiding this comment

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

Thanks

@MaxPatri
Copy link
Contributor Author

Is this normal for check "runtime (Build browser-wasm linux Release LibraryTests)" to be running so long? Or may be it is stuck?
Also I would like to know what should I do with failed checks? I don't see restart button or something else and I assume that my changes could not affect the build or tests.

@eiriktsarpalis
Copy link
Member

It happens from time to time, I've merged main to retrigger a run.

@eiriktsarpalis eiriktsarpalis merged commit 004f205 into dotnet:main Dec 22, 2024
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Text.Json 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.

5 participants