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

Optimization: convert ResolveRequest into readonly struct #1397

Merged
merged 5 commits into from Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Autofac/Core/Container.cs
Expand Up @@ -140,7 +140,7 @@ public ILifetimeScope BeginLoadContextLifetimeScope(object tag, AssemblyLoadCont
public IComponentRegistry ComponentRegistry { get; }

/// <inheritdoc />
public object ResolveComponent(ResolveRequest request)
public object ResolveComponent(in ResolveRequest request)
{
return _rootLifetimeScope.ResolveComponent(request);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Core/ImplicitRegistrationSource.cs
Expand Up @@ -86,7 +86,7 @@ public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Fun
/// <param name="ctx">A component context to resolve services.</param>
/// <param name="request">A resolve request.</param>
/// <returns>An implicit type instance.</returns>
protected abstract object ResolveInstance<T>(IComponentContext ctx, ResolveRequest request)
protected abstract object ResolveInstance<T>(IComponentContext ctx, in ResolveRequest request)
where T : notnull;

/// <summary>
Expand Down
7 changes: 1 addition & 6 deletions src/Autofac/Core/Lifetime/LifetimeScope.cs
Expand Up @@ -323,13 +323,8 @@ private IComponentRegistryBuilder CreateScopeRestrictedRegistry(object tag, Acti
}

/// <inheritdoc />
public object ResolveComponent(ResolveRequest request)
public object ResolveComponent(in ResolveRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}

CheckNotDisposed();

var operation = new ResolveOperation(this, DiagnosticSource);
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Core/Resolving/IResolveOperation.cs
Expand Up @@ -59,5 +59,5 @@ public interface IResolveOperation
/// <param name="currentOperationScope">The scope in the hierarchy in which the operation will begin.</param>
/// <param name="request">The resolve request.</param>
/// <returns>The component instance.</returns>
object GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request);
object GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, in ResolveRequest request);
}
Expand Up @@ -25,14 +25,14 @@ internal sealed class DefaultResolveRequestContext : ResolveRequestContext
/// </param>
internal DefaultResolveRequestContext(
IResolveOperation owningOperation,
ResolveRequest request,
in ResolveRequest request,
ISharingLifetimeScope scope,
DiagnosticListener diagnosticSource)
{
Operation = owningOperation;
ActivationScope = scope;
Parameters = request.Parameters;
_resolveRequest = request ?? throw new ArgumentNullException(nameof(request));
_resolveRequest = request;
PhaseReached = PipelinePhase.ResolveRequestStart;
DiagnosticSource = diagnosticSource;
}
Expand Down Expand Up @@ -90,7 +90,7 @@ internal sealed class DefaultResolveRequestContext : ResolveRequestContext
Parameters = newParameters ?? throw new ArgumentNullException(nameof(newParameters));

/// <inheritdoc />
public override object ResolveComponent(ResolveRequest request) =>
public override object ResolveComponent(in ResolveRequest request) =>
Operation.GetOrCreateInstance(ActivationScope, request);

/// <summary>
Expand Down
Expand Up @@ -95,5 +95,5 @@ public abstract class ResolveRequestContext : IComponentContext
public abstract IComponentRegistry ComponentRegistry { get; }

/// <inheritdoc/>
public abstract object ResolveComponent(ResolveRequest request);
public abstract object ResolveComponent(in ResolveRequest request);
}
13 changes: 4 additions & 9 deletions src/Autofac/Core/Resolving/ResolveOperation.cs
Expand Up @@ -42,7 +42,7 @@ internal sealed class ResolveOperation : IDependencyTrackingResolveOperation
/// Execute the complete resolve operation.
/// </summary>
/// <param name="request">The resolution context.</param>
public object Execute(ResolveRequest request)
public object Execute(in ResolveRequest request)
{
return ExecuteOperation(request);
}
Expand Down Expand Up @@ -93,13 +93,8 @@ public object Execute(ResolveRequest request)
public SegmentedStack<ResolveRequestContext> RequestStack { get; } = new SegmentedStack<ResolveRequestContext>();

/// <inheritdoc />
public object GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
public object GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, in ResolveRequest request)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}

if (_ended)
{
throw new ObjectDisposedException(ResolveOperationResources.TemporaryContextDisposed, innerException: null);
Expand Down Expand Up @@ -170,7 +165,7 @@ public object GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, R
/// </summary>
/// <param name="request">The resolve request.</param>
/// <returns>The resolved instance.</returns>
private object ExecuteOperation(ResolveRequest request)
private object ExecuteOperation(in ResolveRequest request)
{
object result;

Expand Down Expand Up @@ -233,7 +228,7 @@ private object ExecuteOperation(ResolveRequest request)
/// to enable it to be optionally surrounded with diagnostics.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InvokePipeline(ResolveRequest request, DefaultResolveRequestContext requestContext)
private void InvokePipeline(in ResolveRequest request, DefaultResolveRequestContext requestContext)
{
request.ResolvePipeline.Invoke(requestContext);
if (requestContext.Instance == null)
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Diagnostics/DiagnosticSourceExtensions.cs
Expand Up @@ -60,7 +60,7 @@ public static void MiddlewareSuccess(this DiagnosticListener diagnosticSource, R
/// <param name="diagnosticSource">The diagnostic source to which events will be written.</param>
/// <param name="operation">The pipeline resolve operation that is about to run.</param>
/// <param name="initiatingRequest">The request that is responsible for starting this operation.</param>
public static void OperationStart(this DiagnosticListener diagnosticSource, IResolveOperation operation, ResolveRequest initiatingRequest)
public static void OperationStart(this DiagnosticListener diagnosticSource, IResolveOperation operation, in ResolveRequest initiatingRequest)
{
if (diagnosticSource.IsEnabled(DiagnosticEventKeys.OperationStart))
{
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Diagnostics/OperationStartDiagnosticData.cs
Expand Up @@ -15,7 +15,7 @@ public class OperationStartDiagnosticData
/// </summary>
/// <param name="operation">The pipeline resolve operation that is about to run.</param>
/// <param name="initiatingRequest">The request that is responsible for starting this operation.</param>
public OperationStartDiagnosticData(IResolveOperation operation, ResolveRequest initiatingRequest)
public OperationStartDiagnosticData(IResolveOperation operation, in ResolveRequest initiatingRequest)
{
Operation = operation;
InitiatingRequest = initiatingRequest;
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Features/Decorators/DecoratorContext.cs
Expand Up @@ -78,5 +78,5 @@ internal DecoratorContext UpdateContext(object decoratorInstance)
}

/// <inheritdoc />
public object ResolveComponent(ResolveRequest request) => _componentContext.ResolveComponent(request);
public object ResolveComponent(in ResolveRequest request) => _componentContext.ResolveComponent(request);
}
Expand Up @@ -25,9 +25,10 @@ public LazyRegistrationSource()
public override string Description => LazyRegistrationSourceResources.LazyRegistrationSourceDescription;

/// <inheritdoc/>
protected override object ResolveInstance<T>(IComponentContext context, ResolveRequest request)
protected override object ResolveInstance<T>(IComponentContext context, in ResolveRequest request)
{
var capturedContext = context.Resolve<IComponentContext>();
return new Lazy<T>(() => (T)capturedContext.ResolveComponent(request));
ResolveRequest requestCopy = request;
return new Lazy<T>(() => (T)capturedContext.ResolveComponent(requestCopy));
}
}
2 changes: 1 addition & 1 deletion src/Autofac/Features/Metadata/MetaRegistrationSource.cs
Expand Up @@ -24,6 +24,6 @@ public MetaRegistrationSource()
public override string Description => MetaRegistrationSourceResources.MetaRegistrationSourceDescription;

/// <inheritdoc/>
protected override object ResolveInstance<T>(IComponentContext ctx, ResolveRequest request)
protected override object ResolveInstance<T>(IComponentContext ctx, in ResolveRequest request)
=> new Meta<T>((T)ctx.ResolveComponent(request), request.Registration.Target.Metadata);
}
Expand Up @@ -21,7 +21,7 @@ public OwnedInstanceRegistrationSource()
}

/// <inheritdoc/>
protected override object ResolveInstance<T>(IComponentContext ctx, ResolveRequest request)
protected override object ResolveInstance<T>(IComponentContext ctx, in ResolveRequest request)
{
var lifetime = ctx.Resolve<ILifetimeScope>().BeginLifetimeScope(request.Service);
try
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/IComponentContext.cs
Expand Up @@ -27,5 +27,5 @@ public interface IComponentContext
/// </returns>
/// <exception cref="ComponentNotRegisteredException"/>
/// <exception cref="DependencyResolutionException"/>
object ResolveComponent(ResolveRequest request);
object ResolveComponent(in ResolveRequest request);
}
33 changes: 31 additions & 2 deletions src/Autofac/ResolveRequest.cs
Expand Up @@ -9,15 +9,15 @@ namespace Autofac;
/// <summary>
/// The details of an individual request to resolve a service.
/// </summary>
public class ResolveRequest
public readonly struct ResolveRequest : IEquatable<ResolveRequest>
{
/// <summary>
/// Shared constant value defining an empty set of parameters.
/// </summary>
internal static readonly IEnumerable<Parameter> NoParameters = Enumerable.Empty<Parameter>();

/// <summary>
/// Initializes a new instance of the <see cref="ResolveRequest"/> class.
/// Initializes a new instance of the <see cref="ResolveRequest"/> struct.
/// </summary>
/// <param name="service">The service being resolved.</param>
/// <param name="serviceRegistration">The component registration for the service.</param>
Expand Down Expand Up @@ -56,4 +56,33 @@ public ResolveRequest(Service service, ServiceRegistration serviceRegistration,
/// Gets the component registration for the decorator target if configured.
/// </summary>
public IComponentRegistration? DecoratorTarget { get; }

/// <inheritdoc/>
public override bool Equals(object? obj) =>
obj is ResolveRequest other && Equals(other);

/// <inheritdoc/>
public bool Equals(ResolveRequest other) =>
Service == other.Service && Registration == other.Registration && ResolvePipeline == other.ResolvePipeline && Parameters == other.Parameters && DecoratorTarget == other.DecoratorTarget;

/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>The result of the operator.</returns>
public static bool operator ==(ResolveRequest left, ResolveRequest right) => Equals(left, right);

/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>The result of the operator.</returns>
public static bool operator !=(ResolveRequest left, ResolveRequest right) =>
!(left == right);

/// <inheritdoc/>
public override int GetHashCode() =>
Service.GetHashCode() ^ Registration.GetHashCode() ^ ResolvePipeline.GetHashCode() ^ Parameters.GetHashCode() ^ (DecoratorTarget?.GetHashCode() ?? 0);
SergeiPavlov marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 2 additions & 2 deletions test/Autofac.Test/Core/ImplicitRegistrationSourceTests.cs
Expand Up @@ -109,7 +109,7 @@ public AnyTypeImplicitRegistrationSource(Type type)
{
}

protected override object ResolveInstance<T>(IComponentContext ctx, ResolveRequest request) => throw new NotImplementedException();
protected override object ResolveInstance<T>(IComponentContext ctx, in ResolveRequest request) => throw new NotImplementedException();
}

private class Mapped<T>
Expand All @@ -129,7 +129,7 @@ public MappedImplicitRegistrationSource()
{
}

protected override object ResolveInstance<T>(IComponentContext ctx, ResolveRequest request)
protected override object ResolveInstance<T>(IComponentContext ctx, in ResolveRequest request)
{
return new Mapped<T>((T)ctx.ResolveComponent(request));
}
Expand Down
4 changes: 2 additions & 2 deletions test/Autofac.Test/Core/Pipeline/PipelineBuilderTests.cs
Expand Up @@ -483,7 +483,7 @@ protected set

public override IComponentRegistry ComponentRegistry => ActivationScope.ComponentRegistry;

public override object ResolveComponent(ResolveRequest request) => throw new NotImplementedException();
public override object ResolveComponent(in ResolveRequest request) => throw new NotImplementedException();
}

private class LifetimeScopeStub : ISharingLifetimeScope
Expand Down Expand Up @@ -566,7 +566,7 @@ public ValueTask DisposeAsync()
throw new NotImplementedException();
}

public object ResolveComponent(ResolveRequest request)
public object ResolveComponent(in ResolveRequest request)
{
throw new NotImplementedException();
}
Expand Down
11 changes: 2 additions & 9 deletions test/Autofac.Test/Core/Resolving/ResolveOperationTests.cs
Expand Up @@ -33,15 +33,6 @@ public void EmptyInProgressRequestWhenInitializing()
Assert.Empty(inProgressStack);
}

[Fact]
public void GetOrCreateInstanceThrowsArgumentNullExceptionWhenResolveRequestIsNull()
{
var lifetimeScope = Mock.Of<ISharingLifetimeScope>();
var resolveOperation = new ResolveOperation(lifetimeScope, new DiagnosticListener("SomeName"));

Assert.Throws<ArgumentNullException>(() => resolveOperation.GetOrCreateInstance(lifetimeScope, null!));
}

[Fact]
public void AfterTheOperationIsFinished_ReusingTheTemporaryContextThrows()
{
Expand Down Expand Up @@ -77,12 +68,14 @@ public void OperationRaisesSuccessTraceEvents()
var raisedEvents = new List<string>();

var request = new ResolveRequest(new TypedService(typeof(string)), scope.ResolvableImplementationFor<string>(), Enumerable.Empty<Parameter>());
var request2 = new ResolveRequest(new TypedService(typeof(int)), scope.ResolvableImplementationFor<string>(), Enumerable.Empty<Parameter>());

mockTracer.OperationStarting += (op, req) =>
{
raisedEvents.Add("op-start");
Assert.Equal(resolveOp, op);
Assert.Equal(request, req);
Assert.True(req != request2);
};

mockTracer.RequestStarting += (op, context) =>
Expand Down