From 64793f683e1b128ea9a01c737334ca8afe3ceba1 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Jun 2020 02:29:55 +0200 Subject: [PATCH 1/5] Eliminate boxing when entering critical sections Closes #21229 --- ...currencyDetectorCriticalSectionDisposer.cs | 28 +++++++++++++++++ .../Infrastructure/IConcurrencyDetector.cs | 9 ++++-- src/EFCore/Internal/ConcurrencyDetector.cs | 31 ++++++------------- 3 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs diff --git a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs new file mode 100644 index 00000000000..15e0ec1c180 --- /dev/null +++ b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs @@ -0,0 +1,28 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Infrastructure +{ + /// + /// A returned by an , which will exit the ongoing + /// critical section when disposed. + /// + public class ConcurrencyDetectorCriticalSectionDisposer : IDisposable + { + private readonly IConcurrencyDetector _concurrencyDetector; + + /// + /// Constructs a new . + /// + /// + /// The on which the critical section will be exited. + /// + public ConcurrencyDetectorCriticalSectionDisposer(IConcurrencyDetector concurrencyDetector) + => _concurrencyDetector = concurrencyDetector; + + /// + public void Dispose() => _concurrencyDetector.ExitCriticalSection(); + } +} diff --git a/src/EFCore/Infrastructure/IConcurrencyDetector.cs b/src/EFCore/Infrastructure/IConcurrencyDetector.cs index b1f194b4496..17acc0e18b1 100644 --- a/src/EFCore/Infrastructure/IConcurrencyDetector.cs +++ b/src/EFCore/Infrastructure/IConcurrencyDetector.cs @@ -21,9 +21,14 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure public interface IConcurrencyDetector { /// - /// Call to enter the critical section. + /// Enter a critical section. /// /// A disposer that will exit the critical section when disposed. - IDisposable EnterCriticalSection(); + ConcurrencyDetectorCriticalSectionDisposer EnterCriticalSection(); + + /// + /// Exits the critical section. + /// + void ExitCriticalSection(); } } diff --git a/src/EFCore/Internal/ConcurrencyDetector.cs b/src/EFCore/Internal/ConcurrencyDetector.cs index c26e7835574..c58c06af051 100644 --- a/src/EFCore/Internal/ConcurrencyDetector.cs +++ b/src/EFCore/Internal/ConcurrencyDetector.cs @@ -26,7 +26,6 @@ namespace Microsoft.EntityFrameworkCore.Internal /// public class ConcurrencyDetector : IConcurrencyDetector { - private readonly IDisposable _disposer; private int _inCriticalSection; private static readonly AsyncLocal _threadHasLock = new AsyncLocal(); private int _refCount; @@ -37,15 +36,7 @@ public class ConcurrencyDetector : IConcurrencyDetector /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public ConcurrencyDetector() => _disposer = new Disposer(this); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual IDisposable EnterCriticalSection() + public virtual ConcurrencyDetectorCriticalSectionDisposer EnterCriticalSection() { if (Interlocked.CompareExchange(ref _inCriticalSection, 1, 0) == 1) { @@ -60,10 +51,16 @@ public virtual IDisposable EnterCriticalSection() } _refCount++; - return _disposer; + return new ConcurrencyDetectorCriticalSectionDisposer(this); } - private void ExitCriticalSection() + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual void ExitCriticalSection() { Check.DebugAssert(_inCriticalSection == 1, "Expected to be in a critical section"); @@ -73,15 +70,5 @@ private void ExitCriticalSection() _inCriticalSection = 0; } } - - private readonly struct Disposer : IDisposable - { - private readonly ConcurrencyDetector _concurrencyDetector; - - public Disposer(ConcurrencyDetector concurrencyDetector) - => _concurrencyDetector = concurrencyDetector; - - public void Dispose() => _concurrencyDetector.ExitCriticalSection(); - } } } From ad77941e8ca1e3d77cce3518203626e7b1ff006f Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Jun 2020 03:02:27 +0200 Subject: [PATCH 2/5] Update src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs Co-authored-by: Smit Patel --- .../ConcurrencyDetectorCriticalSectionDisposer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs index 15e0ec1c180..c5ba905e99c 100644 --- a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs +++ b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs @@ -6,8 +6,8 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// - /// A returned by an , which will exit the ongoing - /// critical section when disposed. + /// A returned by an , which will exit the ongoing + /// critical section when disposed. /// public class ConcurrencyDetectorCriticalSectionDisposer : IDisposable { From f1f4bb88a3dec0e002f8dcba8a342ef0613fc532 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Jun 2020 03:02:38 +0200 Subject: [PATCH 3/5] Update src/EFCore/Infrastructure/IConcurrencyDetector.cs Co-authored-by: Andriy Svyryd --- src/EFCore/Infrastructure/IConcurrencyDetector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore/Infrastructure/IConcurrencyDetector.cs b/src/EFCore/Infrastructure/IConcurrencyDetector.cs index 17acc0e18b1..95c87514cf9 100644 --- a/src/EFCore/Infrastructure/IConcurrencyDetector.cs +++ b/src/EFCore/Infrastructure/IConcurrencyDetector.cs @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure public interface IConcurrencyDetector { /// - /// Enter a critical section. + /// Enters a critical section. /// /// A disposer that will exit the critical section when disposed. ConcurrencyDetectorCriticalSectionDisposer EnterCriticalSection(); From 1d71f0f0c3ac017524dde4c3a3e76cb899050f68 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Jun 2020 03:08:53 +0200 Subject: [PATCH 4/5] Make it a struct... --- .../ConcurrencyDetectorCriticalSectionDisposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs index c5ba905e99c..c28ad5d6cf0 100644 --- a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs +++ b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure /// A returned by an , which will exit the ongoing /// critical section when disposed. /// - public class ConcurrencyDetectorCriticalSectionDisposer : IDisposable + public readonly struct ConcurrencyDetectorCriticalSectionDisposer : IDisposable { private readonly IConcurrencyDetector _concurrencyDetector; From dc1ed35c843152ccfc59476327d1693e75589f98 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 12 Jun 2020 03:23:53 +0200 Subject: [PATCH 5/5] Hopefully last fixes --- .../ConcurrencyDetectorCriticalSectionDisposer.cs | 5 +++-- .../Infrastructure/EntityFrameworkServicesBuilderTest.cs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs index c28ad5d6cf0..d0f12680162 100644 --- a/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs +++ b/src/EFCore/Infrastructure/ConcurrencyDetectorCriticalSectionDisposer.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using JetBrains.Annotations; namespace Microsoft.EntityFrameworkCore.Infrastructure { @@ -14,12 +15,12 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure private readonly IConcurrencyDetector _concurrencyDetector; /// - /// Constructs a new . + /// Constructs a new . /// /// /// The on which the critical section will be exited. /// - public ConcurrencyDetectorCriticalSectionDisposer(IConcurrencyDetector concurrencyDetector) + public ConcurrencyDetectorCriticalSectionDisposer([NotNull] IConcurrencyDetector concurrencyDetector) => _concurrencyDetector = concurrencyDetector; /// diff --git a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs index adab826b9e7..d9a2914ced2 100644 --- a/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs +++ b/test/EFCore.Tests/Infrastructure/EntityFrameworkServicesBuilderTest.cs @@ -316,12 +316,12 @@ private static void TestMultipleScoped(Action tr private class FakeConcurrencyDetector : IConcurrencyDetector { - public IDisposable EnterCriticalSection() + ConcurrencyDetectorCriticalSectionDisposer IConcurrencyDetector.EnterCriticalSection() { throw new NotImplementedException(); } - public Task EnterCriticalSectionAsync(CancellationToken cancellationToken) + public void ExitCriticalSection() { throw new NotImplementedException(); }