diff --git a/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs b/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs index 7c7fb29..7292475 100644 --- a/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs +++ b/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs @@ -41,9 +41,9 @@ public sealed class PooledObjectBuffer : IEnumerable where T : PooledObject { #if (NET35 || NET40) - private const MethodImplOptions TryToInline = default(MethodImplOptions); + private const MethodImplOptions TryInline = default(MethodImplOptions); #else - private const MethodImplOptions TryToInline = MethodImplOptions.AggressiveInlining; + private const MethodImplOptions TryInline = MethodImplOptions.AggressiveInlining; #endif /// @@ -106,7 +106,7 @@ public IEnumerator GetEnumerator() /// /// Output pooled object. /// True if has a value, false otherwise. - [MethodImpl(TryToInline)] + [MethodImpl(TryInline)] public bool TryDequeue(out T pooledObject) { for (var i = 0; i < _pooledObjects.Length; i++) @@ -127,7 +127,7 @@ public bool TryDequeue(out T pooledObject) /// /// Input pooled object. /// True if there was enough space to enqueue given object, false otherwise. - [MethodImpl(TryToInline)] + [MethodImpl(TryInline)] public bool TryEnqueue(T pooledObject) { for (var i = 0; i < _pooledObjects.Length; i++) @@ -186,5 +186,24 @@ public IList Resize(int newCapacity) Array.Resize(ref _pooledObjects, newCapacity); return exceedingItems; } + + /// + /// Tries to remove given object from the buffer. + /// + /// Pooled object to be removed. + /// True if has been removed, false otherwise. + [MethodImpl(TryInline)] + public bool TryRemove(T pooledObject) + { + for (var i = 0; i < _pooledObjects.Length; i++) + { + var item = _pooledObjects[i]; + if (item != null && item == pooledObject && Interlocked.CompareExchange(ref _pooledObjects[i], null, item) == item) + { + return true; + } + } + return false; + } } } \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/ObjectPool.cs b/src/CodeProject.ObjectPool/ObjectPool.cs index a5b4b64..fdd1e94 100644 --- a/src/CodeProject.ObjectPool/ObjectPool.cs +++ b/src/CodeProject.ObjectPool/ObjectPool.cs @@ -245,14 +245,18 @@ void IObjectPoolHandle.ReturnObjectToPool(PooledObject objectToReturnToPool, boo #endregion Pool Operations - #region Private Methods + #region Protected Methods /// /// Keeps track of last pooled object ID. /// private int _lastPooledObjectId; - private T CreatePooledObject() + /// + /// Creates a new pooled object, initializing its info. + /// + /// A new pooled object. + protected T CreatePooledObject() { if (Diagnostics.Enabled) { @@ -271,7 +275,11 @@ private T CreatePooledObject() return newObject; } - private void DestroyPooledObject(PooledObject objectToDestroy) + /// + /// Destroys given pooled object, disposing its resources. + /// + /// The pooled object that should be destroyed. + protected void DestroyPooledObject(PooledObject objectToDestroy) { // Making sure that the object is only disposed once (in case of application shutting // down and we don't control the order of the finalization). @@ -293,6 +301,6 @@ private void DestroyPooledObject(PooledObject objectToDestroy) GC.SuppressFinalize(objectToDestroy); } - #endregion Private Methods + #endregion Protected Methods } } \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/PooledObject.cs b/src/CodeProject.ObjectPool/PooledObject.cs index 624ae29..f4abd6c 100644 --- a/src/CodeProject.ObjectPool/PooledObject.cs +++ b/src/CodeProject.ObjectPool/PooledObject.cs @@ -199,6 +199,10 @@ private void HandleReAddingToPool(bool reRegisterForFinalization) protected override IEnumerable> GetFormattingMembers() { yield return new KeyValuePair(nameof(PooledObjectInfo.Id), PooledObjectInfo.Id); + if (PooledObjectInfo.Payload != null) + { + yield return new KeyValuePair(nameof(PooledObjectInfo.Payload), PooledObjectInfo.Payload); + } } /// diff --git a/src/CodeProject.ObjectPool/TimedObjectPool.cs b/src/CodeProject.ObjectPool/TimedObjectPool.cs index e74fd7b..7098b4b 100644 --- a/src/CodeProject.ObjectPool/TimedObjectPool.cs +++ b/src/CodeProject.ObjectPool/TimedObjectPool.cs @@ -23,6 +23,8 @@ #if !(NETSTD10 || NETSTD11) +using System; +using System.Linq; using System.Threading; namespace CodeProject.ObjectPool @@ -37,7 +39,59 @@ namespace CodeProject.ObjectPool internal class TimedObjectPool : ObjectPool, ITimedObjectPool where T : PooledObject { - private readonly Timer Timer; + private TimeSpan _timeout; + private Timer _timer; + + public TimedObjectPool() + { + } + + /// + /// When pooled objects have not been used for a time greater than , + /// then they will be destroyed by a cleaning task. + /// + public TimeSpan Timeout + { + get => _timeout; + set + { + _timeout = value; + UpdateTimeout(); + } + } + + /// + /// Updates the timer according to a new timeout. + /// + private void UpdateTimeout() + { + lock (this) + { + if (_timer != null) + { + // A timer already exists, simply change its period. + _timer.Change(_timeout, _timeout); + return; + } + + _timer = new Timer(_ => + { + // Local copy, since the buffer might change. + var items = PooledObjects.ToArray(); + + // All items which have been last used before following threshold will be destroyed. + var threshold = DateTime.UtcNow - _timeout; + + foreach (var item in items) + { + if (item.PooledObjectInfo.Payload is DateTime lastUsage && lastUsage < threshold && PooledObjects.TryRemove(item)) + { + DestroyPooledObject(item); + } + } + }, null, _timeout, _timeout); + } + } } } diff --git a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj index e0b1b67..a376cf1 100644 --- a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj +++ b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj @@ -16,7 +16,7 @@ - +