From b9912fee6b8082459fc33788c50916316944a2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sat, 19 Jan 2019 20:00:52 +0100 Subject: [PATCH] DisposableObjectPool added \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6bcb1aee83d64225b24416b7c5e8d78f204ae2f0 --- src/ObjectPool/src/DefaultObjectPool.cs | 30 +++++-- src/ObjectPool/src/DisposableObjectPool.cs | 70 ++++++++++++++++ .../test/DisposableObjectPoolTest.cs | 83 +++++++++++++++++++ 3 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 src/ObjectPool/src/DisposableObjectPool.cs create mode 100644 src/ObjectPool/test/DisposableObjectPoolTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index dcd7f1c715..6ecc923140 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -10,10 +10,10 @@ namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPool : ObjectPool where T : class { - private readonly ObjectWrapper[] _items; + protected readonly ObjectWrapper[] _items; private readonly IPooledObjectPolicy _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 _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; } diff --git a/src/ObjectPool/src/DisposableObjectPool.cs b/src/ObjectPool/src/DisposableObjectPool.cs new file mode 100644 index 0000000000..ca9288a6d6 --- /dev/null +++ b/src/ObjectPool/src/DisposableObjectPool.cs @@ -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 : DefaultObjectPool, IDisposable where T : class + { + private volatile bool _isDisposed; + + public DisposableObjectPool(IPooledObjectPolicy policy) + : base(policy) + { + } + + public DisposableObjectPool(IPooledObjectPolicy 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(); + } + } + } +} diff --git a/src/ObjectPool/test/DisposableObjectPoolTest.cs b/src/ObjectPool/test/DisposableObjectPoolTest.cs new file mode 100644 index 0000000000..4bc9142efb --- /dev/null +++ b/src/ObjectPool/test/DisposableObjectPoolTest.cs @@ -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(new DefaultPooledObjectPolicy()); + 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(new DefaultPooledObjectPolicy()); + 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(new DefaultPooledObjectPolicy()); + var obj1 = pool.Get(); + var obj2 = pool.Get(); + pool.Return(obj1); + pool.Return(obj2); + + // Act + pool.Dispose(); + + // Assert + Assert.Throws(() => pool.Get()); + } + + [Fact] + public void DisposableObjectPool_DisposeAndReturn_DisposesObject() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + 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; + } + } +}