Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,6 @@ $RECYCLE.BIN/

# JetBrains Rider
.idea

# Reasonix AI assistant
.reasonix/
55 changes: 45 additions & 10 deletions src/Autofac.Pooling/PoolActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ namespace Autofac.Pooling;
internal sealed class PoolActivator<TLimit> : IInstanceActivator
where TLimit : class
{
private readonly Service _pooledInstanceService;
private readonly IPooledRegistrationPolicy<TLimit> _policy;
private readonly DefaultObjectPoolProvider _poolProvider;
private readonly Service? _pooledInstanceService;
private readonly IPooledRegistrationPolicy<TLimit>? _policy;
private readonly DefaultObjectPoolProvider? _poolProvider;
private readonly Func<IComponentContext, IPooledRegistrationPolicy<TLimit>>? _policyFactory;

/// <summary>
/// Initializes a new instance of the <see cref="PoolActivator{TLimit}"/> class.
/// Initializes a new instance of the <see cref="PoolActivator{TLimit}"/> class
/// using the default <see cref="DefaultObjectPoolProvider"/>.
/// </summary>
/// <param name="pooledInstanceService">The service used to resolve new instances of the pooled registration.</param>
/// <param name="policy">The pool policy.</param>
Expand All @@ -34,6 +36,23 @@ public PoolActivator(Service pooledInstanceService, IPooledRegistrationPolicy<TL
};
}

/// <summary>
/// Initializes a new instance of the <see cref="PoolActivator{TLimit}"/> class
/// using a factory function to create the <see cref="IPooledRegistrationPolicy{TLimit}"/> at resolve time.
/// The default <see cref="DefaultObjectPoolProvider"/> will create the backing pool.
/// </summary>
/// <param name="pooledInstanceService">The service used to resolve new instances of the pooled registration.</param>
/// <param name="policyFactory">
/// A factory that returns the <see cref="IPooledRegistrationPolicy{TLimit}"/> to use.
/// Invoked during resolve, so the <see cref="IComponentContext"/> is available
/// for resolving dependencies.
/// </param>
public PoolActivator(Service pooledInstanceService, Func<IComponentContext, IPooledRegistrationPolicy<TLimit>> policyFactory)
{
_pooledInstanceService = pooledInstanceService;
_policyFactory = policyFactory ?? throw new ArgumentNullException(nameof(policyFactory));
}

/// <inheritdoc/>
public Type LimitType { get; } = typeof(TLimit);

Expand All @@ -42,15 +61,31 @@ public void ConfigurePipeline(IComponentRegistryServices componentRegistryServic
{
pipelineBuilder.Use(PipelinePhase.Activation, (context, next) =>
{
// Get a reference to the actual lifetime scope.
var scope = context.Resolve<ILifetimeScope>();
if (_policyFactory is not null)
Comment thread
tillig marked this conversation as resolved.
{
// Custom policy factory: resolve the strategy at resolve time, then create DefaultObjectPool.
var policy = _policyFactory(context);
var poolProvider = new DefaultObjectPoolProvider
{
MaximumRetained = policy.MaximumRetained,
};
var scope = context.Resolve<ILifetimeScope>();
var poolPolicy = new AutofacPooledObjectPolicy<TLimit>(_pooledInstanceService!, scope, policy);
var pool = poolProvider.Create(poolPolicy);
context.Instance = new PooledInstanceContext<TLimit>(pool, policy);
}
else
{
// Default path: use DefaultObjectPoolProvider + AutofacPooledObjectPolicy.
var scope = context.Resolve<ILifetimeScope>();

var poolPolicy = new AutofacPooledObjectPolicy<TLimit>(_pooledInstanceService, scope, _policy);
var poolPolicy = new AutofacPooledObjectPolicy<TLimit>(_pooledInstanceService!, scope, _policy!);

// The pool provider will create a disposable pool if the TLimit implements IDisposable.
var pool = _poolProvider.Create(poolPolicy);
// The pool provider will create a disposable pool if the TLimit implements IDisposable.
var pool = _poolProvider!.Create(poolPolicy);

context.Instance = pool;
context.Instance = pool;
}
});
}

Expand Down
21 changes: 17 additions & 4 deletions src/Autofac.Pooling/PoolGetActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal sealed class PoolGetActivator<TLimit> : IInstanceActivator
where TLimit : class
{
private readonly PoolService _poolService;
private readonly IPooledRegistrationPolicy<TLimit> _registrationPolicy;
private readonly IPooledRegistrationPolicy<TLimit>? _registrationPolicy;

/// <summary>
/// Initializes a new instance of the <see cref="PoolGetActivator{TLimit}"/> class.
Expand All @@ -30,6 +30,16 @@ public PoolGetActivator(PoolService poolService, IPooledRegistrationPolicy<TLimi
_registrationPolicy = registrationPolicy;
}

/// <summary>
/// Initializes a new instance of the <see cref="PoolGetActivator{TLimit}"/> class.
/// The policy will be retrieved from the <see cref="PooledInstanceContext{TLimit}"/> at resolve time.
/// </summary>
/// <param name="poolService">The service used to access the pool.</param>
public PoolGetActivator(PoolService poolService)
{
_poolService = poolService;
}

/// <inheritdoc/>
public Type LimitType { get; } = typeof(TLimit);

Expand All @@ -38,7 +48,10 @@ public void ConfigurePipeline(IComponentRegistryServices componentRegistryServic
{
pipelineBuilder.Use(PipelinePhase.Activation, (ctxt, next) =>
{
var pool = (ObjectPool<TLimit>)ctxt.ResolveService(_poolService);
var resolved = ctxt.ResolveService(_poolService);
var ctx = resolved as PooledInstanceContext<TLimit>;
var pool = ctx is not null ? ctx.Pool : (ObjectPool<TLimit>)resolved;
var policy = ctx is not null ? ctx.Policy : _registrationPolicy!;
var didGetFromPool = false;

TLimit PoolGet()
Expand All @@ -47,11 +60,11 @@ TLimit PoolGet()
return pool.Get();
}

var poolItem = _registrationPolicy.Get(ctxt, ctxt.Parameters, PoolGet) ?? throw new InvalidOperationException(
var poolItem = policy.Get(ctxt, ctxt.Parameters, PoolGet) ?? throw new InvalidOperationException(
string.Format(
CultureInfo.CurrentCulture,
PoolGetActivatorResources.PolicyMustReturnInstance,
_registrationPolicy.GetType().FullName,
policy.GetType().FullName,
typeof(TLimit).FullName));

if (didGetFromPool)
Expand Down
42 changes: 42 additions & 0 deletions src/Autofac.Pooling/PooledInstanceContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.Extensions.ObjectPool;

namespace Autofac.Pooling;

/// <summary>
/// Holds a resolved pool and its associated policy instance,
/// so the policy created during pool construction is shared with the get-side activator.
/// </summary>
/// <typeparam name="TLimit">The limit type of the objects in the pool.</typeparam>
internal sealed class PooledInstanceContext<TLimit>
where TLimit : class
{
/// <summary>
/// Initializes a new instance of the <see cref="PooledInstanceContext{TLimit}"/> class.
/// </summary>
/// <param name="pool">The object pool.</param>
/// <param name="policy">The resolved registration policy.</param>
public PooledInstanceContext(ObjectPool<TLimit> pool, IPooledRegistrationPolicy<TLimit> policy)
{
Pool = pool;
Policy = policy;
}

/// <summary>
/// Gets the object pool.
/// </summary>
public ObjectPool<TLimit> Pool
{
get;
}

/// <summary>
/// Gets the resolved registration policy, shared between pool creation and retrieval.
/// </summary>
public IPooledRegistrationPolicy<TLimit> Policy
{
get;
}
}
Loading
Loading