Skip to content

Commit

Permalink
Switch to ConcurrentDictionary
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriySvyryd committed Jan 22, 2021
1 parent 04a8ab0 commit c0685a7
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public RelationalModelRuntimeInitializer(
}

/// <summary>
/// The relational dependencies.
/// The relational dependencies.
/// </summary>
protected virtual RelationalModelRuntimeInitializerDependencies RelationalDependencies { get; }

Expand Down
78 changes: 45 additions & 33 deletions src/EFCore/Infrastructure/Annotatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;

Expand All @@ -26,7 +29,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
public class Annotatable : IMutableAnnotatable
{
private SortedDictionary<string, Annotation>? _annotations;
private SortedDictionary<string, Annotation>? _runtimeAnnotations;
private ConcurrentDictionary<string, Annotation>? _runtimeAnnotations;

/// <summary>
/// <para>Indicates whether the current object is read-only.</para>
Expand Down Expand Up @@ -239,7 +242,9 @@ protected virtual Annotation CreateAnnotation([NotNull] string name, [CanBeNull]
/// Gets all runtime annotations on the current object.
/// </summary>
public virtual IEnumerable<Annotation> GetRuntimeAnnotations()
=> _runtimeAnnotations?.Values ?? Enumerable.Empty<Annotation>();
=> _runtimeAnnotations == null
? Enumerable.Empty<Annotation>()
: _runtimeAnnotations.OrderBy(p => p.Key).Select(p => p.Value);

/// <summary>
/// Adds a runtime annotation to this object. Throws if an annotation with the specified name already exists.
Expand All @@ -263,16 +268,9 @@ public virtual Annotation AddRuntimeAnnotation([NotNull] string name, [CanBeNull
/// <param name="annotation"> The annotation to be added. </param>
/// <returns> The added annotation. </returns>
protected virtual Annotation AddRuntimeAnnotation([NotNull] string name, [NotNull] Annotation annotation)
{
if (FindRuntimeAnnotation(name) != null)
{
throw new InvalidOperationException(CoreStrings.DuplicateAnnotation(name, ToString()));
}

SetRuntimeAnnotation(name, annotation, oldAnnotation: null);

return annotation;
}
=> GetOrCreateRuntimeAnnotations().TryAdd(name, annotation)
? annotation
: throw new InvalidOperationException(CoreStrings.DuplicateAnnotation(name, ToString()));

/// <summary>
/// Sets the runtime annotation stored under the given key. Overwrites the existing annotation if an
Expand All @@ -281,16 +279,11 @@ protected virtual Annotation AddRuntimeAnnotation([NotNull] string name, [NotNul
/// <param name="name"> The key of the annotation to be added. </param>
/// <param name="value"> The value to be stored in the annotation. </param>
public virtual Annotation SetRuntimeAnnotation([NotNull] string name, [CanBeNull] object? value)
{
var oldAnnotation = FindRuntimeAnnotation(name);
if (oldAnnotation != null
&& Equals(oldAnnotation.Value, value))
{
return oldAnnotation;
}

return SetRuntimeAnnotation(name, CreateRuntimeAnnotation(name, value), oldAnnotation);
}
=> GetOrCreateRuntimeAnnotations().AddOrUpdate(name,
static (n, a) => a.Annotatable.CreateRuntimeAnnotation(n, a.Value),
static (n, oldAnnotation, a) =>
!Equals(oldAnnotation.Value, a.Value) ? a.Annotatable.CreateRuntimeAnnotation(n, a.Value) : oldAnnotation,
(Value: value, Annotatable: this));

/// <summary>
/// Sets the runtime annotation stored under the given key. Overwrites the existing annotation if an
Expand All @@ -305,18 +298,30 @@ protected virtual Annotation SetRuntimeAnnotation(
[NotNull] Annotation annotation,
[CanBeNull] Annotation? oldAnnotation)
{
EnsureReadOnly();

if (_runtimeAnnotations == null)
{
_runtimeAnnotations = new SortedDictionary<string, Annotation>();
}

_runtimeAnnotations[name] = annotation;
GetOrCreateRuntimeAnnotations()[name] = annotation;

return annotation;
}

/// <summary>
/// Gets the value of the runtime annotation with the given name, adding it if one does not exist.
/// </summary>
/// <param name="name"> The name of the annotation. </param>
/// <param name="valueFactory"> The factory used to create the value if the annotation doesn't exist. </param>
/// <param name="factoryArgument"> An argument for the factory method. </param>
/// <returns>
/// The value of the existing runtime annotation if an annotation with the specified name already exists.
/// Otherwise a newly created value.
/// </returns>
public virtual TValue GetOrAddRuntimeAnnotationValue<TValue, TArg>(
string name,
Func<TArg, TValue> valueFactory,
TArg factoryArgument)
=> (TValue)GetOrCreateRuntimeAnnotations().GetOrAdd(
name,
static (n, t) => t.Annotatable.CreateRuntimeAnnotation(n, t.CreateValue(t.Argument)),
(CreateValue: valueFactory, Argument: factoryArgument, Annotatable: this)).Value;

/// <summary>
/// Gets the runtime annotation with the given name, returning <see langword="null" /> if it does not exist.
/// </summary>
Expand Down Expand Up @@ -345,13 +350,12 @@ protected virtual Annotation SetRuntimeAnnotation(
Check.NotNull(name, nameof(name));
EnsureReadOnly();

var annotation = FindRuntimeAnnotation(name);
if (annotation == null)
if (_runtimeAnnotations == null)
{
return null;
}

_runtimeAnnotations!.Remove(name);
_runtimeAnnotations.Remove(name, out var annotation);

return annotation;
}
Expand All @@ -365,6 +369,14 @@ protected virtual Annotation SetRuntimeAnnotation(
protected virtual Annotation CreateRuntimeAnnotation([NotNull] string name, [CanBeNull] object? value)
=> new Annotation(name, value);

private ConcurrentDictionary<string, Annotation> GetOrCreateRuntimeAnnotations()
{
EnsureReadOnly();

return NonCapturingLazyInitializer.EnsureInitialized(
ref _runtimeAnnotations, (object?)null, static _ => new ConcurrentDictionary<string, Annotation>());
}

/// <inheritdoc />
[DebuggerStepThrough]
IEnumerable<IAnnotation> IAnnotatable.GetAnnotations()
Expand Down
16 changes: 16 additions & 0 deletions src/EFCore/Infrastructure/IAnnotatable.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using JetBrains.Annotations;

Expand Down Expand Up @@ -90,5 +91,20 @@ public interface IAnnotatable
/// <param name="name"> The name of the annotation to remove. </param>
/// <returns> The annotation that was removed. </returns>
IAnnotation? RemoveRuntimeAnnotation([NotNull] string name);

/// <summary>
/// Gets the value of the runtime annotation with the given name, adding it if one does not exist.
/// </summary>
/// <param name="name"> The name of the annotation. </param>
/// <param name="valueFactory"> The factory used to create the value if the annotation doesn't exist. </param>
/// <param name="factoryArgument"> An argument for the factory method. </param>
/// <returns>
/// The value of the existing runtime annotation if an annotation with the specified name already exists.
/// Otherwise a newly created value.
/// </returns>
TValue GetOrAddRuntimeAnnotationValue<TValue, TArg>(
[NotNull] string name,
[NotNull] Func<TArg, TValue> valueFactory,
[CanBeNull] TArg factoryArgument);
}
}
2 changes: 1 addition & 1 deletion src/EFCore/Metadata/IModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ bool SetModelDependencies([NotNull] SingletonModelDependencies modelDependencies
return false;
}

SetRuntimeAnnotation(CoreAnnotationNames.ModelDependencies, modelDependencies);
AddRuntimeAnnotation(CoreAnnotationNames.ModelDependencies, modelDependencies);

return true;
}
Expand Down

0 comments on commit c0685a7

Please sign in to comment.