parent
c960040b53
commit
b9912fee6b
|
|
@ -10,10 +10,10 @@ namespace Microsoft.Extensions.ObjectPool
|
|||
{
|
||||
public class DefaultObjectPool<T> : ObjectPool<T> where T : class
|
||||
{
|
||||
private readonly ObjectWrapper[] _items;
|
||||
protected readonly ObjectWrapper[] _items;
|
||||
private readonly IPooledObjectPolicy<T> _policy;
|
||||
private readonly bool _isDefaultPolicy;
|
||||
private T _firstItem;
|
||||
protected T _firstItem;
|
||||
|
||||
// This class was introduced in 2.1 to avoid the interface call where possible
|
||||
private readonly PooledObjectPolicy<T> _fastPolicy;
|
||||
|
|
@ -71,29 +71,43 @@ namespace Microsoft.Extensions.ObjectPool
|
|||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private T Create() => _fastPolicy?.Create() ?? _policy.Create();
|
||||
|
||||
public override void Return(T obj)
|
||||
public override void Return(T obj) => ReturnCore(obj);
|
||||
|
||||
protected bool ReturnCore(T obj)
|
||||
{
|
||||
if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))
|
||||
{
|
||||
if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)
|
||||
if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null)
|
||||
{
|
||||
ReturnViaScan(obj);
|
||||
return true; // returned to pool
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReturnViaScan(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not returned to pool
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ReturnViaScan(T obj)
|
||||
private bool ReturnViaScan(T obj)
|
||||
{
|
||||
var items = _items;
|
||||
for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref items[i].Element, obj, null) == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array.
|
||||
[DebuggerDisplay("{Element}")]
|
||||
private struct ObjectWrapper
|
||||
protected struct ObjectWrapper
|
||||
{
|
||||
public T Element;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
// 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.Threading;
|
||||
|
||||
namespace Microsoft.Extensions.ObjectPool
|
||||
{
|
||||
public class DisposableObjectPool<T> : DefaultObjectPool<T>, IDisposable where T : class
|
||||
{
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
public DisposableObjectPool(IPooledObjectPolicy<T> policy)
|
||||
: base(policy)
|
||||
{
|
||||
}
|
||||
|
||||
public DisposableObjectPool(IPooledObjectPolicy<T> policy, int maximumRetained)
|
||||
: base(policy, maximumRetained)
|
||||
{
|
||||
}
|
||||
|
||||
public override T Get()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
ThrowObjectDisposedException();
|
||||
}
|
||||
|
||||
return base.Get();
|
||||
|
||||
void ThrowObjectDisposedException()
|
||||
{
|
||||
throw new ObjectDisposedException(this.GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Return(T obj)
|
||||
{
|
||||
// When the pool is disposed or the obj is not returned to the pool, dispose it
|
||||
if (_isDisposed || !ReturnCore(obj))
|
||||
{
|
||||
DisposeItem(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeItem(_firstItem);
|
||||
_firstItem = null;
|
||||
|
||||
ObjectWrapper[] items = _items;
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
DisposeItem(items[i].Element);
|
||||
items[i].Element = null;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
private void DisposeItem(T item)
|
||||
{
|
||||
if (item is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.Extensions.ObjectPool
|
||||
{
|
||||
public class DisposableObjectPoolTest
|
||||
{
|
||||
[Fact]
|
||||
public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed()
|
||||
{
|
||||
// Arrange
|
||||
var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
|
||||
var obj = pool.Get();
|
||||
pool.Return(obj);
|
||||
|
||||
// Act
|
||||
pool.Dispose();
|
||||
|
||||
// Assert
|
||||
Assert.True(obj.IsDisposed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposableObjectPoolWithTwoElements_Dispose_ObjectsDisposed()
|
||||
{
|
||||
// Arrange
|
||||
var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
|
||||
var obj1 = pool.Get();
|
||||
var obj2 = pool.Get();
|
||||
pool.Return(obj1);
|
||||
pool.Return(obj2);
|
||||
|
||||
// Act
|
||||
pool.Dispose();
|
||||
|
||||
// Assert
|
||||
Assert.True(obj1.IsDisposed);
|
||||
Assert.True(obj2.IsDisposed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposableObjectPool_DisposeAndGet_ThrowsObjectDisposed()
|
||||
{
|
||||
// Arrange
|
||||
var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
|
||||
var obj1 = pool.Get();
|
||||
var obj2 = pool.Get();
|
||||
pool.Return(obj1);
|
||||
pool.Return(obj2);
|
||||
|
||||
// Act
|
||||
pool.Dispose();
|
||||
|
||||
// Assert
|
||||
Assert.Throws<ObjectDisposedException>(() => pool.Get());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisposableObjectPool_DisposeAndReturn_DisposesObject()
|
||||
{
|
||||
// Arrange
|
||||
var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
|
||||
var obj = pool.Get();
|
||||
|
||||
// Act
|
||||
pool.Dispose();
|
||||
pool.Return(obj);
|
||||
|
||||
// Assert
|
||||
Assert.True(obj.IsDisposed);
|
||||
}
|
||||
|
||||
private class DisposableObject : IDisposable
|
||||
{
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public void Dispose() => IsDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue