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

CSHARP-5349: Fix discriminator convention inheritance. #1512

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

rstam
Copy link
Contributor

@rstam rstam commented Oct 23, 2024

No description provided.

@rstam rstam requested a review from a team as a code owner October 23, 2024 23:44

if (classMap._isRootClass)
{
return StandardDiscriminatorConvention.Hierarchical;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is still wrong... there just isn't a test to show that it is.

Consider this scenario: there might be a registered discriminator convention at this level and we're not looking it up.

if (classMap._isRootClass)
{
// in this case auto-register a hierarchical convention for the root class and look it up as usual below
BsonSerializer.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Calling GetOrRegisterDiscriminatorConvention instead of RegsiterDiscriminatorConvention is in order to avoid collisions between threads.

Copy link
Contributor

Choose a reason for hiding this comment

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

How does it avoid collisions?

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 new GetOrRegisterDiscriminatorConvention method uses the same lock as RegsiterDiscriminatorConvention to handle thread safety.

@@ -269,6 +269,43 @@ public static object Deserialize(TextReader textReader, Type nominalType, Action
}
}

internal static IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, IDiscriminatorConvention discriminatorConvention)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These don't need to be public.

Comment on lines 274 to 285
__configLock.EnterReadLock();
try
{
if (__discriminatorConventions.TryGetValue(type, out var registeredConvention))
{
return registeredConvention;
}
}
finally
{
__configLock.ExitReadLock();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this whole block really needed? Seems a bit redundant to check for a registered convention here and then check again later on below when trying to register.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may seem redundant but it's not. All the other methods use the same pattern.

The idea is that after the initial configuration phase is over everything can be done with read lock only, eliminating lock contention between threads.

The second check below is because of multithreading. Between releasing the read lock and acquiring the write lock some other thread might have snuck in and registered the convention.

Copy link
Contributor

@adelinowona adelinowona left a comment

Choose a reason for hiding this comment

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

LGTM

@rstam rstam requested review from papafe and damieng October 28, 2024 16:03
@rstam rstam requested a review from sanych-sun November 1, 2024 02:57
if (classMap._isRootClass)
{
// in this case auto-register a hierarchical convention for the root class and look it up as usual below
BsonSerializer.GetOrRegisterDiscriminatorConvention(classMap._classType, StandardDiscriminatorConvention.Hierarchical);
Copy link
Member

Choose a reason for hiding this comment

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

Does GetOrRegisterDiscriminatorConvention returns the value we are looking for? Can we return it straight away without additional lookup below?

{
return registeredConvention;
}
else
Copy link
Member

Choose a reason for hiding this comment

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

Minor: this else block if unnecessary as there is return in true path of the check. Can simplify it to reduce the nesting level.

@@ -269,6 +269,43 @@ public static object Deserialize(TextReader textReader, Type nominalType, Action
}
}

internal static IDiscriminatorConvention GetOrRegisterDiscriminatorConvention(Type type, IDiscriminatorConvention discriminatorConvention)
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest to have slightly different method signature to mimic ConcurrentDictionary.GetOrAdd method signature:

internal static IDiscriminatorConvention GetOrAddDiscriminatorConvention(Type type, Func<Type, IDiscriminatorConvention> discriminatorConventionFactory)

Copy link
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

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

LGTM

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.

4 participants