diff --git a/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs b/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs index 6dd6e7c..984fb68 100644 --- a/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs +++ b/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs @@ -53,6 +53,11 @@ public sealed class PooledObjectInfo : IEquatable /// internal IObjectPoolHandle Handle { get; set; } + /// + /// The number of times the object was borrowed + /// + public long BorrowCount{get; internal set; } + /// /// Returns a string that represents the current object. /// diff --git a/src/CodeProject.ObjectPool/Core/PooledObjectState.cs b/src/CodeProject.ObjectPool/Core/PooledObjectState.cs index ba903e3..2785f4b 100644 --- a/src/CodeProject.ObjectPool/Core/PooledObjectState.cs +++ b/src/CodeProject.ObjectPool/Core/PooledObjectState.cs @@ -44,6 +44,21 @@ public enum PooledObjectState : byte /// /// The object has been disposed and cannot be used anymore. /// - Disposed = 2 + Disposed = 2, + + /// + /// The object is outside the pool, waiting to validate. + /// + Validating = 4, + + /// + /// The object is outside the pool,waiting to be used. + /// + ValidateSuccess = 8, + + /// + /// The object is outside the pool,waiting to destory. + /// + ValidateFail = 16 } } \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/EvictionConfig.cs b/src/CodeProject.ObjectPool/EvictionConfig.cs new file mode 100644 index 0000000..496c4c8 --- /dev/null +++ b/src/CodeProject.ObjectPool/EvictionConfig.cs @@ -0,0 +1,29 @@ +// Copyright (c) GZNB. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodeProject.ObjectPool +{ + /// + /// Eviction Config Infomation + /// + public class EvictionConfig + { + /// + /// Eviction Is Enable default is false + /// + public bool Enable { get; set; } = false; + + /// + /// Timer delay time default is zero + /// + public TimeSpan Delay { get; set; } = TimeSpan.Zero; + + /// + /// Timer period default is one minute + /// + public TimeSpan Period { get; set; } = TimeSpan.FromMinutes(1); + } +} \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/EvictorTimer.cs b/src/CodeProject.ObjectPool/EvictorTimer.cs new file mode 100644 index 0000000..6125512 --- /dev/null +++ b/src/CodeProject.ObjectPool/EvictorTimer.cs @@ -0,0 +1,118 @@ +// Copyright (c) GZNB. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +#if !NET35 +using CodeProject.ObjectPool.Logging; +#endif + + +namespace CodeProject.ObjectPool +{ +#if !NETSTD10 + public class EvictorTimer : IEvictionTimer, IDisposable + { +#if !NET35 + private static readonly ILog Log = LogProvider.GetLogger(typeof(EvictorTimer)); +#endif + private Dictionary _actionMap; + private volatile bool _disposed; + + public EvictorTimer() + { + this._actionMap = new Dictionary(); + } + + /// 执行与释放或重置非托管资源关联的应用程序定义的任务。 + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Add Eviction action + /// + /// eviction action + /// delay time + /// period + public void Schedule(Action action, TimeSpan delay, TimeSpan period) + { + this.ThrowIfDisposed(); + if (action == null) + { + return; + } + lock (typeof(EvictorTimer)) + { +#if!NET35 + Action piplineAction = () => Log.Debug("Begin Schedule Evictor"); + piplineAction += action; + piplineAction += () => Log.Debug("End Schedule Evictor"); + action = piplineAction; +#endif + if (this._actionMap.TryGetValue(action, out Timer timer)) + { + timer.Change(delay, period); + } + else + { + var t = new Timer(state => action(), null, delay, period); + this._actionMap[action] = t; + } + } + } + + /// + /// Cancle Action + /// + /// + public void Cancel(Action task) + { + this.ThrowIfDisposed(); + lock (typeof(EvictorTimer)) + { + if (this._actionMap.TryGetValue(task, out Timer timer)) + { + this._actionMap.Remove(task); + timer.Dispose(); + } + } + } + + private void ThrowIfDisposed() + { + if (this._disposed) + { + throw new ObjectDisposedException(this.GetType().FullName); + } + } + + ~EvictorTimer() + { + this.Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!this._disposed) + { + this._disposed = true; + if (disposing) + { + Timer[] timers = this._actionMap?.Values.ToArray() ?? new Timer[0]; + this._actionMap.Clear(); + this._actionMap = null; + foreach (Timer t in timers) + { + t.Dispose(); + } + } + } + } + } +#endif +} \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/IEvictionTimer.cs b/src/CodeProject.ObjectPool/IEvictionTimer.cs new file mode 100644 index 0000000..cbc66bc --- /dev/null +++ b/src/CodeProject.ObjectPool/IEvictionTimer.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodeProject.ObjectPool +{ + /// + /// Eviction Timer Interface + /// + public interface IEvictionTimer + { + /// + /// Add Eviction action + /// + /// eviction action + /// delay time + /// period + void Schedule(Action action, TimeSpan delay, TimeSpan period); + + /// + /// Cancle Action + /// + /// + void Cancel(Action task); + } +} diff --git a/src/CodeProject.ObjectPool/ObjectPool.cs b/src/CodeProject.ObjectPool/ObjectPool.cs index 10feb17..354f783 100644 --- a/src/CodeProject.ObjectPool/ObjectPool.cs +++ b/src/CodeProject.ObjectPool/ObjectPool.cs @@ -75,7 +75,7 @@ public ObjectPool(Func factoryMethod) /// /// is less than or equal to zero. /// - public ObjectPool(int maximumPoolSize, Func factoryMethod) + public ObjectPool(int maximumPoolSize, Func factoryMethod, IEvictionTimer timer = null, EvictionConfig evictionConfig = null) { // Preconditions if (maximumPoolSize <= 0) throw new ArgumentOutOfRangeException(nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize); @@ -90,6 +90,13 @@ public ObjectPool(int maximumPoolSize, Func factoryMethod) // Creating a new instance for the Diagnostics class. Diagnostics = new ObjectPoolDiagnostics(); + + +#if !NETSTD10 + // Creating a new instance for the EvictorTimer class. + this._timer = timer ?? new EvictorTimer(); +#endif + this.StartEvictor(evictionConfig); } #endregion C'tor and Initialization code @@ -158,6 +165,32 @@ public int MaximumPoolSize #region Pool Operations + /// + /// Start Evictor + /// + /// + protected void StartEvictor(EvictionConfig config) + { + if (this._timer != null && config.Enable) + { + this._timer.Schedule( + () => + { + if (this.PooledObjects.TryDequeue(out T pooledObject)) + { + if (pooledObject.ValidateObject(PooledObjectValidationContext.Outbound)) + { + pooledObject.PooledObjectInfo.State = PooledObjectState.Available; + } + else + { + this.DestroyPooledObject(pooledObject); + } + } + }, config.Delay, config.Period); + } + } + /// /// Clears the pool and destroys each object stored inside it. /// @@ -197,8 +230,13 @@ public T GetObject() // as available as soon as the object will return to the pool. pooledObject.PooledObjectInfo.State = PooledObjectState.Reserved; + pooledObject.PooledObjectInfo.BorrowCount++; return pooledObject; } + else + { + this.DestroyPooledObject(pooledObject); + } } } @@ -261,6 +299,8 @@ void IObjectPoolHandle.ReturnObjectToPool(PooledObject objectToReturnToPool, boo /// private int _lastPooledObjectId; + private IEvictionTimer _timer; + /// /// Creates a new pooled object, initializing its info. /// diff --git a/src/CodeProject.ObjectPool/PooledObject.cs b/src/CodeProject.ObjectPool/PooledObject.cs index c498e32..349f306 100644 --- a/src/CodeProject.ObjectPool/PooledObject.cs +++ b/src/CodeProject.ObjectPool/PooledObject.cs @@ -57,9 +57,18 @@ internal bool ValidateObject(PooledObjectValidationContext validationContext) { if (OnValidateObject != null) { + this.PooledObjectInfo.State = PooledObjectState.Validating; try { - return OnValidateObject(validationContext); + bool verifyResult = OnValidateObject(validationContext); + if (verifyResult) + { + this.PooledObjectInfo.State = PooledObjectState.ValidateSuccess; + } + else + { + this.PooledObjectInfo.State = PooledObjectState.ValidateFail; + } } catch (Exception ex) {