Skip to content

Commit

Permalink
Remove indirection in DependencyInjection (#52140)
Browse files Browse the repository at this point in the history
- Remove indirection
- Get rid of scope state and lock on ResolvedServices
- Don't run customer code under the lock
- No public API changes
  • Loading branch information
pakrym authored May 26, 2021
1 parent ef6096d commit ecdb90e
Show file tree
Hide file tree
Showing 21 changed files with 232 additions and 369 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,6 @@ public void ServiceRealizationFailed(string? exceptionMessage)
WriteEvent(6, exceptionMessage);
}

[NonEvent]
public void ScopeDisposed(ServiceProviderEngine engine, ScopeState state)
{
if (IsEnabled(EventLevel.Verbose, EventKeywords.All))
{
ScopeDisposed(engine.GetHashCode(), state.ResolvedServicesCount, state.DisposableServicesCount);
}
}

[NonEvent]
public void ServiceResolved(Type serviceType)
{
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,7 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi
throw new ArgumentNullException(nameof(options));
}

IServiceProviderEngine engine;

#if !NETSTANDARD2_1
engine = new DynamicServiceProviderEngine(services);
#else
if (RuntimeFeature.IsDynamicCodeCompiled)
{
engine = new DynamicServiceProviderEngine(services);
}
else
{
// Don't try to compile Expressions/IL if they are going to get interpreted
engine = new RuntimeServiceProviderEngine(services);
}
#endif

return new ServiceProvider(services, engine, options);
return new ServiceProvider(services, options);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>
{
public static CallSiteRuntimeResolver Instance { get; } = new();

private CallSiteRuntimeResolver()
{
}

public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
{
return VisitCallSite(callSite, new RuntimeResolverContext
Expand Down Expand Up @@ -66,7 +72,7 @@ protected override object VisitRootCache(ServiceCallSite callSite, RuntimeResolv
}

var lockType = RuntimeResolverLock.Root;
ServiceProviderEngineScope serviceProviderEngine = context.Scope.Engine.Root;
ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;

lock (callSite)
{
Expand All @@ -91,7 +97,7 @@ protected override object VisitScopeCache(ServiceCallSite callSite, RuntimeResol
{
// Check if we are in the situation where scoped service was promoted to singleton
// and we need to lock the root
return context.Scope == context.Scope.Engine.Root ?
return context.Scope.IsRootScope ?
VisitRootCache(callSite, context) :
VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);
}
Expand Down Expand Up @@ -149,7 +155,7 @@ protected override object VisitServiceProvider(ServiceProviderCallSite servicePr

protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context)
{
return context.Scope.Engine;
return serviceScopeFactoryCallSite.Value;
}

protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
Expand All @@ -14,21 +13,11 @@ internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine
public ExpressionResolverBuilder ResolverBuilder { get; }
#endif

public CompiledServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors)
: base(serviceDescriptors)
public CompiledServiceProviderEngine(ServiceProvider provider)
{
#if IL_EMIT
ResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
#else
ResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
#endif
ResolverBuilder = new(provider);
}

protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
Func<ServiceProviderEngineScope, object> realizedService = ResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;
return realizedService;
}
public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite) => ResolverBuilder.Build(callSite);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Threading;

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine
{
public DynamicServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors)
: base(serviceDescriptors)
private readonly ServiceProvider _serviceProvider;

public DynamicServiceProviderEngine(ServiceProvider serviceProvider): base(serviceProvider)
{
_serviceProvider = serviceProvider;
}

protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
int callCount = 0;
return scope =>
Expand All @@ -29,7 +30,7 @@ protected override Func<ServiceProviderEngineScope, object> RealizeService(Servi
// Resolve the result before we increment the call count, this ensures that singletons
// won't cause any side effects during the compilation of the resolve function.
var result = RuntimeResolver.Resolve(callSite, scope);
var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
if (Interlocked.Increment(ref callCount) == 2)
{
Expand All @@ -46,7 +47,7 @@ protected override Func<ServiceProviderEngineScope, object> RealizeService(Servi
{
try
{
base.RealizeService(callSite);
_serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,21 @@ internal sealed class ExpressionResolverBuilder : CallSiteVisitor<object, Expres
Expression.Call(ScopeParameter, CaptureDisposableMethodInfo, CaptureDisposableParameter),
CaptureDisposableParameter);

private readonly CallSiteRuntimeResolver _runtimeResolver;

private readonly IServiceScopeFactory _serviceScopeFactory;

private readonly ServiceProviderEngineScope _rootScope;

private readonly ConcurrentDictionary<ServiceCacheKey, Func<ServiceProviderEngineScope, object>> _scopeResolverCache;

private readonly Func<ServiceCacheKey, ServiceCallSite, Func<ServiceProviderEngineScope, object>> _buildTypeDelegate;

public ExpressionResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope)
public ExpressionResolverBuilder(ServiceProvider serviceProvider)
{
if (runtimeResolver == null)
{
throw new ArgumentNullException(nameof(runtimeResolver));
}

_rootScope = serviceProvider.Root;
_scopeResolverCache = new ConcurrentDictionary<ServiceCacheKey, Func<ServiceProviderEngineScope, object>>();
_runtimeResolver = runtimeResolver;
_serviceScopeFactory = serviceScopeFactory;
_rootScope = rootScope;
_buildTypeDelegate = (key, cs) => BuildNoCache(cs);
}

public Func<ServiceProviderEngineScope, object> Build(ServiceCallSite callSite)
{
// Optimize singleton case
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
object value = _runtimeResolver.Resolve(callSite, _rootScope);
return scope => value;
}

// Only scope methods are cached
if (callSite.Cache.Location == CallSiteResultCacheLocation.Scope)
{
Expand Down Expand Up @@ -117,7 +99,7 @@ private Expression<Func<ServiceProviderEngineScope, object>> BuildExpression(Ser

protected override Expression VisitRootCache(ServiceCallSite singletonCallSite, object context)
{
return Expression.Constant(_runtimeResolver.Resolve(singletonCallSite, _rootScope));
return Expression.Constant(CallSiteRuntimeResolver.Instance.Resolve(singletonCallSite, _rootScope));
}

protected override Expression VisitConstant(ConstantCallSite constantCallSite, object context)
Expand All @@ -132,7 +114,7 @@ protected override Expression VisitServiceProvider(ServiceProviderCallSite servi

protected override Expression VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, object context)
{
return Expression.Constant(_serviceScopeFactory);
return Expression.Constant(serviceScopeFactoryCallSite.Value);
}

protected override Expression VisitFactory(FactoryCallSite factoryCallSite, object context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine
{
private readonly ExpressionResolverBuilder _expressionResolverBuilder;
public ExpressionsServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors) : base(serviceDescriptors)

public ExpressionsServiceProviderEngine(ServiceProvider serviceProvider)
{
_expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
_expressionResolverBuilder = new ExpressionResolverBuilder(serviceProvider);
}

protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
Func<ServiceProviderEngineScope, object> realizedService = _expressionResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;
return realizedService;
return _expressionResolverBuilder.Build(callSite);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ internal sealed class ILEmitResolverBuilder : CallSiteVisitor<ILEmitResolverBuil

private sealed class ILEmitResolverBuilderRuntimeContext
{
public IServiceScopeFactory ScopeFactory;
public object[] Constants;
public Func<IServiceProvider, object>[] Factories;
}
Expand All @@ -38,39 +37,21 @@ private struct GeneratedMethod
public DynamicMethod DynamicMethod;
}

private readonly CallSiteRuntimeResolver _runtimeResolver;

private readonly IServiceScopeFactory _serviceScopeFactory;

private readonly ServiceProviderEngineScope _rootScope;

private readonly ConcurrentDictionary<ServiceCacheKey, GeneratedMethod> _scopeResolverCache;

private readonly Func<ServiceCacheKey, ServiceCallSite, GeneratedMethod> _buildTypeDelegate;

public ILEmitResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope) :
base()
public ILEmitResolverBuilder(ServiceProvider serviceProvider)
{
if (runtimeResolver == null)
{
throw new ArgumentNullException(nameof(runtimeResolver));
}
_runtimeResolver = runtimeResolver;
_serviceScopeFactory = serviceScopeFactory;
_rootScope = rootScope;
_rootScope = serviceProvider.Root;
_scopeResolverCache = new ConcurrentDictionary<ServiceCacheKey, GeneratedMethod>();
_buildTypeDelegate = (key, cs) => BuildTypeNoCache(cs);
}

public Func<ServiceProviderEngineScope, object> Build(ServiceCallSite callSite)
{
// Optimize singleton case
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
object value = _runtimeResolver.Resolve(callSite, _rootScope);
return scope => value;
}

return BuildType(callSite).Lambda;
}

Expand Down Expand Up @@ -166,7 +147,7 @@ protected override object VisitConstructor(ConstructorCallSite constructorCallSi

protected override object VisitRootCache(ServiceCallSite callSite, ILEmitResolverBuilderContext argument)
{
AddConstant(argument, _runtimeResolver.Resolve(callSite, _rootScope));
AddConstant(argument, CallSiteRuntimeResolver.Instance.Resolve(callSite, _rootScope));
return null;
}

Expand Down Expand Up @@ -205,9 +186,7 @@ protected override object VisitServiceProvider(ServiceProviderCallSite servicePr

protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ILEmitResolverBuilderContext argument)
{
// this.ScopeFactory
argument.Generator.Emit(OpCodes.Ldarg_0);
argument.Generator.Emit(OpCodes.Ldfld, typeof(ILEmitResolverBuilderRuntimeContext).GetField(nameof(ILEmitResolverBuilderRuntimeContext.ScopeFactory)));
AddConstant(argument, serviceScopeFactoryCallSite.Value);
return null;
}

Expand Down Expand Up @@ -426,8 +405,7 @@ private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(ServiceCallSite c
return new ILEmitResolverBuilderRuntimeContext
{
Constants = context.Constants?.ToArray(),
Factories = context.Factories?.ToArray(),
ScopeFactory = _serviceScopeFactory
Factories = context.Factories?.ToArray()
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine
{
private readonly ILEmitResolverBuilder _expressionResolverBuilder;
public ILEmitServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors) : base(serviceDescriptors)
public ILEmitServiceProviderEngine(ServiceProvider serviceProvider)
{
_expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
_expressionResolverBuilder = new ILEmitResolverBuilder(serviceProvider);
}

protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
Func<ServiceProviderEngineScope, object> realizedService = _expressionResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;
return realizedService;
return _expressionResolverBuilder.Build(callSite);
}
}
}

This file was deleted.

Loading

0 comments on commit ecdb90e

Please sign in to comment.