diff --git a/CHANGELOG.md b/CHANGELOG.md
index fefb414..69781a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog for CodeProject.ObjectPool #
+### v3.0.3 (2017-04-08) ###
+
+* Added a timed object pool (issue #1).
+* OnReleaseResources and OnResetState are now simple actions on PooledObject.
+
### v3.0.2 (2017-04-02) ###
* Moved core pool buffer into dedicated class: Core.PooledObjectBuffer.
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 0000000..a605ae4
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ObjectPool.sln b/ObjectPool.sln
index 2fdfbc2..1de97e7 100644
--- a/ObjectPool.sln
+++ b/ObjectPool.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.26228.9
+VisualStudioVersion = 15.0.26403.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{4C2B7C0C-7CDD-4125-B57A-88E168D24190}"
ProjectSection(SolutionItems) = preProject
@@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{4C2B7C0C-7
build.cake = build.cake
CHANGELOG.md = CHANGELOG.md
LICENSE.htm = LICENSE.htm
+ NuGet.Config = NuGet.Config
pomma89.snk = pomma89.snk
README.md = README.md
EndProjectSection
diff --git a/README.md b/README.md
index ba32bfc..1b5fe1b 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ A generic, concurrent, portable and flexible Object Pool for the .NET Framework,
## Summary ##
-* Latest release version: `v3.0.2`
+* Latest release version: `v3.0.3`
* Build status on [AppVeyor](https://ci.appveyor.com): [![Build status](https://ci.appveyor.com/api/projects/status/r4qnqaqj9ri6cicn?svg=true)](https://ci.appveyor.com/project/pomma89/objectpool)
* [Doxygen](http://www.stack.nl/~dimitri/doxygen/index.html) documentation:
+ [HTML](https://goo.gl/RVA7mV)
@@ -37,9 +37,9 @@ internal static class Program
///
private static void Main()
{
- // Creating a pool with minimum size of 5 and maximum size of 25, using custom Factory
- // method to create and instance of ExpensiveResource.
- var pool = new ObjectPool(5, 25, () => new ExpensiveResource(/* resource specific initialization */));
+ // Creating a pool with a maximum size of 25, using custom Factory method to create and
+ // instance of ExpensiveResource.
+ var pool = new ObjectPool(25, () => new ExpensiveResource(/* resource specific initialization */));
using (var resource = pool.GetObject())
{
@@ -52,8 +52,8 @@ internal static class Program
var newPool = new ObjectPool>(() =>
new PooledObjectWrapper(CreateNewResource())
{
- WrapperReleaseResourcesAction = r => ExternalResourceReleaseResource(r),
- WrapperResetStateAction = r => ExternalResourceResetState(r)
+ OnReleaseResources = ExternalResourceReleaseResource,
+ OnResetState = ExternalResourceResetState
});
using (var wrapper = newPool.GetObject())
@@ -61,6 +61,22 @@ internal static class Program
// wrapper.InternalResource contains the object that you pooled.
wrapper.InternalResource.DoOtherStuff();
} // Exiting the using scope will return the object back to the pool.
+
+ // Creates a pool where objects which have not been used for over 2 seconds will be
+ // cleaned up by a dedicated thread.
+ var timedPool = new TimedObjectPool(TimeSpan.FromSeconds(2));
+
+ using (var resource = timedPool.GetObject())
+ {
+ // Using the resource...
+ resource.DoStuff();
+ } // Exiting the using scope will return the object back to the pool and record last usage.
+
+ Console.WriteLine($"Timed pool size after 0 seconds: {timedPool.ObjectsInPoolCount}"); // Should be 1
+ Thread.Sleep(TimeSpan.FromSeconds(4));
+ Console.WriteLine($"Timed pool size after 4 seconds: {timedPool.ObjectsInPoolCount}"); // Should be 0
+
+ Console.Read();
}
private static ExternalExpensiveResource CreateNewResource()
@@ -81,19 +97,22 @@ internal static class Program
internal sealed class ExpensiveResource : PooledObject
{
- public void DoStuff()
+ public ExpensiveResource()
{
- // Do some work here, for example.
- }
+ OnReleaseResources = () =>
+ {
+ // Called if the resource needs to be manually cleaned before the memory is reclaimed.
+ };
- protected override void OnReleaseResources()
- {
- // Override if the resource needs to be manually cleaned before the memory is reclaimed.
+ OnResetState = () =>
+ {
+ // Called if the resource needs resetting before it is getting back into the pool.
+ };
}
- protected override void OnResetState()
+ public void DoStuff()
{
- // Override if the resource needs resetting before it is getting back into the pool.
+ // Do some work here, for example.
}
}
@@ -112,91 +131,89 @@ All benchmarks were implemented and run using the wonderful [BenchmarkDotNet](ht
### [Retrieve one object](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool.Benchmarks/RetrieveOneObject.cs) ###
-In this benchmark we evaluate how long it takes to extract and return an object stored into the pool, using a single thread. We compare three implementations:
+In this benchmark we evaluate how long it takes to extract and return an object stored into the pool, using a single thread. We compare four implementations:
* [This project's ObjectPool](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool/ObjectPool.cs)
* [This project's ParameterizedObjectPool](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool/ParameterizedObjectPool.cs)
* [Microsoft's ObjectPool](http://www.nuget.org/packages/Microsoft.Extensions.ObjectPool/)
+* [Original ObjectPool](http://www.codeproject.com/Articles/535735/Implementing-a-Generic-Object-Pool-in-NET)
-```ini
+``` ini
-Host Process Environment Information:
-BenchmarkDotNet.Core=v0.9.9.0
-OS=Microsoft Windows NT 6.2.9200.0
-Processor=Intel(R) Core(TM) i3-2330M CPU 2.20GHz, ProcessorCount=4
-Frequency=14318180 ticks, Resolution=69.8413 ns, Timer=HPET
-CLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
-GC=Concurrent Workstation
-JitModules=clrjit-v4.6.1586.0
+BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
+Processor=AMD A10 Extreme Edition Radeon R8, 4C+8G, ProcessorCount=4
+Frequency=1949470 Hz, Resolution=512.9599 ns, Timer=TSC
+ [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
+ RyuJitX64 : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
-Type=RetrieveOneObject Mode=Throughput
+Job=RyuJitX64 Jit=RyuJit Platform=X64
```
- Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
------------------------- |------------ |---------- |------ |------ |------ |------------------- |
- SimpleObjectPool | 136.0373 ns | 3.4367 ns | 11.02 | 9.58 | 9.58 | 3,09 |
- ParameterizedObjectPool | 199.7968 ns | 4.4435 ns | 21.00 | - | - | 3,08 |
- MicrosoftObjectPool | 60.7935 ns | 1.8593 ns | - | - | - | 0,00 |
+ | Method | Mean | StdDev | Gen 0 | Allocated |
+ |------------------------ |-------------- |----------- |------- |---------- |
+ | SimpleObjectPool | 106.3367 ns | 1.9033 ns | - | 0 B |
+ | ParameterizedObjectPool | 174.2507 ns | 1.9017 ns | 0.0391 | 24 B |
+ | MicrosoftObjectPool | 59.3673 ns | 1.2349 ns | - | 0 B |
+ | OriginalObjectPool | 1,773.9186 ns | 96.7615 ns | 0.0238 | 240 B |
### [Retrieve objects concurrently](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool.Benchmarks/RetrieveObjectsConcurrently.cs) ###
-In this benchmark we evaluate how long it takes to extract and return an object stored into the pool, using `Count` threads. We compare three implementations:
+In this benchmark we evaluate how long it takes to extract and return an object stored into the pool, using `Count` threads. We compare four implementations:
* [This project's ObjectPool](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool/ObjectPool.cs)
* [This project's ParameterizedObjectPool](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool/ParameterizedObjectPool.cs)
* [Microsoft's ObjectPool](http://www.nuget.org/packages/Microsoft.Extensions.ObjectPool/)
+* [Original ObjectPool](http://www.codeproject.com/Articles/535735/Implementing-a-Generic-Object-Pool-in-NET)
-```ini
+``` ini
-Host Process Environment Information:
-BenchmarkDotNet.Core=v0.9.9.0
-OS=Microsoft Windows NT 6.2.9200.0
-Processor=Intel(R) Core(TM) i3-2330M CPU 2.20GHz, ProcessorCount=4
-Frequency=14318180 ticks, Resolution=69.8413 ns, Timer=HPET
-CLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
-GC=Concurrent Workstation
-JitModules=clrjit-v4.6.1586.0
+BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
+Processor=AMD A10 Extreme Edition Radeon R8, 4C+8G, ProcessorCount=4
+Frequency=1949470 Hz, Resolution=512.9599 ns, Timer=TSC
+ [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
+ RyuJitX64 : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
-Type=RetrieveObjectsConcurrently Mode=Throughput Affinity=2
+Job=RyuJitX64 Jit=RyuJit Platform=X64
```
- Method | Count | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
------------------------- |------ |------------ |---------- |------ |------ |------ |------------------- |
- **SimpleObjectPool** | **10** | **5.7390 us** | **0.3109 us** | **1.72** | **0.72** | **0.07** | **278,68** |
- ParameterizedObjectPool | 10 | 6.4189 us | 1.1792 us | 1.58 | 1.23 | - | 284,70 |
- MicrosoftObjectPool | 10 | 4.2985 us | 0.0975 us | 1.30 | 1.11 | - | 244,84 |
- **SimpleObjectPool** | **100** | **19.6048 us** | **1.2525 us** | **3.04** | **2.32** | **0.73** | **576,85** |
- ParameterizedObjectPool | 100 | 27.8705 us | 1.9570 us | 3.09 | 2.81 | - | 560,35 |
- MicrosoftObjectPool | 100 | 10.5264 us | 1.5110 us | 1.78 | 0.97 | 0.03 | 233,25 |
- **SimpleObjectPool** | **1000** | **157.9986 us** | **6.2393 us** | **12.67** | **12.12** | **9.92** | **3.277,64** |
- ParameterizedObjectPool | 1000 | 223.4054 us | 7.6698 us | 20.00 | - | - | 2.697,82 |
- MicrosoftObjectPool | 1000 | 76.3736 us | 0.8204 us | 5.17 | 0.29 | - | 247,38 |
+ | Method | Count | Mean | StdDev | Gen 0 | Gen 1 | Allocated |
+ |------------------------ |------ |-------------- |----------- |-------- |-------- |---------- |
+ | **SimpleObjectPool** | **10** | **10.1346 us** | **0.3799 us** | **1.7548** | **-** | **1.12 kB** |
+ | ParameterizedObjectPool | 10 | 13.6740 us | 0.3560 us | 1.9409 | - | 1.42 kB |
+ | MicrosoftObjectPool | 10 | 9.3135 us | 0.1154 us | 1.7008 | - | 1.12 kB |
+ | OriginalObjectPool | 10 | 26.4688 us | 0.7511 us | - | - | 3.7 kB |
+ | **SimpleObjectPool** | **100** | **54.1560 us** | **1.7507 us** | **-** | **-** | **1.31 kB** |
+ | ParameterizedObjectPool | 100 | 72.3400 us | 0.8960 us | 4.8177 | - | 3.93 kB |
+ | MicrosoftObjectPool | 100 | 30.9284 us | 1.1752 us | 2.9975 | - | 2.16 kB |
+ | OriginalObjectPool | 100 | 177.0052 us | 3.7795 us | 1.7904 | - | 27.04 kB |
+ | **SimpleObjectPool** | **1000** | **689.5726 us** | **21.9610 us** | **-** | **-** | **4.07 kB** |
+ | ParameterizedObjectPool | 1000 | 844.7284 us | 19.1851 us | 10.9863 | - | 28.18 kB |
+ | MicrosoftObjectPool | 1000 | 362.2347 us | 21.3030 us | 51.1351 | 15.1438 | 28.3 kB |
+ | OriginalObjectPool | 1000 | 1,458.5456 us | 25.8381 us | 29.1667 | - | 268.99 kB |
+
### [Memory stream pooling](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool.Benchmarks/MemoryStreamPooling.cs) ###
-In this benchmark we evaluate how long it takes to extract and return a memory stream stored into the pool, using a single thread. We compare three implementations:
+In this benchmark we evaluate how long it takes to extract and return a memory stream stored into the pool, using a single thread. We compare two implementations:
* [This project's MemoryStreamPool](https://github.com/pomma89/ObjectPool/blob/master/ObjectPool/Specialized/MemoryStreamPool.cs)
* [Microsoft's RecyclableMemoryStreamManager](http://www.nuget.org/packages/Microsoft.IO.RecyclableMemoryStream/)
-```ini
+``` ini
-Host Process Environment Information:
-BenchmarkDotNet.Core=v0.9.9.0
-OS=Microsoft Windows NT 6.2.9200.0
-Processor=Intel(R) Core(TM) i3-2330M CPU 2.20GHz, ProcessorCount=4
-Frequency=14318180 ticks, Resolution=69.8413 ns, Timer=HPET
-CLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
-GC=Concurrent Workstation
-JitModules=clrjit-v4.6.1586.0
+BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0
+Processor=AMD A10 Extreme Edition Radeon R8, 4C+8G, ProcessorCount=4
+Frequency=1949470 Hz, Resolution=512.9599 ns, Timer=TSC
+ [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
+ RyuJitX64 : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
-Type=MemoryStreamPooling Mode=Throughput
+Job=RyuJitX64 Jit=RyuJit Platform=X64
```
- Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
------------------------------- |-------------- |------------ |------- |------ |------ |------------------- |
- MemoryStreamPool | 171.7594 ns | 4.7127 ns | 1.46 | 1.26 | 1.26 | 3,08 |
- RecyclableMemoryStreamManager | 2,924.0579 ns | 117.2652 ns | 341.00 | - | - | 88,74 |
+ | Method | Mean | StdErr | StdDev | Gen 0 | Allocated |
+ |------------------------------ |-------------- |----------- |------------ |------- |---------- |
+ | MemoryStreamPool | 180.0207 ns | 1.6126 ns | 6.0337 ns | - | 0 B |
+ | RecyclableMemoryStreamManager | 4,213.5708 ns | 44.6009 ns | 446.0087 ns | 0.8134 | 448 B |
## About this repository and its maintainer ##
diff --git a/appveyor.yml b/appveyor.yml
index f86f786..5740c9b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,7 +9,7 @@
#---------------------------------#
# version format
-version: 3.0.2.{build}
+version: 3.0.3.{build}
# branches to build
branches:
@@ -25,7 +25,7 @@ branches:
assembly_info:
patch: true
file: AssemblyInfo.*
- assembly_version: "3.0.2.{build}"
+ assembly_version: "3.0.3.{build}"
assembly_file_version: "{version}"
assembly_informational_version: "{version}"
diff --git a/build.cake b/build.cake
index 1d91f0c..c2bee66 100644
--- a/build.cake
+++ b/build.cake
@@ -92,12 +92,12 @@ private void Build(string cfg)
// NoIncremental = true
// });
//}
-
- MSBuild(solutionFile, settings =>
- {
+
+ MSBuild(solutionFile, settings =>
+ {
settings.SetConfiguration(cfg);
- settings.SetMaxCpuCount(0);
- });
+ settings.SetMaxCpuCount(0);
+ });
}
private void Test(string cfg)
@@ -107,27 +107,27 @@ private void Test(string cfg)
// NoResults = true
//});
- const string flags = "--noheader --noresult";
- const string errMsg = " - Unit test failure - ";
-
- Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.exe".Replace("{cfg}", cfg)), netExe =>
- {
- if (StartProcess(netExe, flags) != 0)
- {
- throw new Exception(cfg + errMsg + netExe);
- }
- });
-
- Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.dll".Replace("{cfg}", cfg)), netCoreDll =>
- {
- DotNetCoreExecute(netCoreDll, flags);
- });
+ const string flags = "--noheader --noresult";
+ const string errMsg = " - Unit test failure - ";
+
+ Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.exe".Replace("{cfg}", cfg)), netExe =>
+ {
+ if (StartProcess(netExe, flags) != 0)
+ {
+ throw new Exception(cfg + errMsg + netExe);
+ }
+ });
+
+ Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.dll".Replace("{cfg}", cfg)), netCoreDll =>
+ {
+ DotNetCoreExecute(netCoreDll, flags);
+ });
}
private void Pack(string cfg)
{
- Parallel.ForEach(GetFiles("./src/**/*.csproj"), project =>
- {
+ Parallel.ForEach(GetFiles("./src/**/*.csproj"), project =>
+ {
//DotNetCorePack(project.FullPath, new DotNetCorePackSettings
//{
// Configuration = cfg,
@@ -135,15 +135,15 @@ private void Pack(string cfg)
// NoBuild = true
//});
- MSBuild(project, settings =>
- {
- settings.SetConfiguration(cfg);
- settings.SetMaxCpuCount(0);
- settings.WithTarget("pack");
- settings.WithProperty("IncludeSymbols", new[] { "true" });
- });
-
- var packDir = project.GetDirectory().Combine("bin").Combine(cfg);
- MoveFiles(GetFiles(packDir + "/*.nupkg"), artifactsDir);
- });
+ MSBuild(project, settings =>
+ {
+ settings.SetConfiguration(cfg);
+ settings.SetMaxCpuCount(0);
+ settings.WithTarget("pack");
+ settings.WithProperty("IncludeSymbols", new[] { "true" });
+ });
+
+ var packDir = project.GetDirectory().Combine("bin").Combine(cfg);
+ MoveFiles(GetFiles(packDir + "/*.nupkg"), artifactsDir);
+ });
}
\ No newline at end of file
diff --git a/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj b/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj
index 52d66f9..5d34eb3 100644
--- a/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj
+++ b/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj
@@ -2,7 +2,7 @@
CodeProject.ObjectPool
Generic and concurrent Object Pool
- 3.0.2
+ 3.0.3
netstandard1.0;netstandard1.1;netstandard1.2;netstandard1.3;net35;net40;net45
true
../../pomma89.snk
@@ -80,8 +80,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/CodeProject.ObjectPool/Core/ErrorMessages.cs b/src/CodeProject.ObjectPool/Core/ErrorMessages.cs
index 0115b6d..9800be3 100644
--- a/src/CodeProject.ObjectPool/Core/ErrorMessages.cs
+++ b/src/CodeProject.ObjectPool/Core/ErrorMessages.cs
@@ -29,7 +29,7 @@ namespace CodeProject.ObjectPool.Core
internal static class ErrorMessages
{
public const string NegativeOrZeroMaximumPoolSize = "Maximum pool size must be greater than zero.";
- public const string NullDiagnostics = "Pool diagnostics recorder cannot be null.";
+ public const string NegativeOrZeroTimeout = "Timeout must be greater than zero.";
public const string NullResource = "Resource cannot be null.";
}
}
\ No newline at end of file
diff --git a/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs b/src/CodeProject.ObjectPool/Core/PooledObjectBuffer.cs
index 7c7fb29..d3b726d 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
///
@@ -83,9 +83,8 @@ public int Count
/// An enumerator that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
- for (var i = 0; i <= _pooledObjects.Length; ++i)
+ foreach (var item in _pooledObjects)
{
- var item = _pooledObjects[i];
if (item != null)
{
yield return item;
@@ -106,7 +105,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 +126,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 +185,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/ITimedObjectPool.cs b/src/CodeProject.ObjectPool/ITimedObjectPool.cs
index 08605a8..0ac5668 100644
--- a/src/CodeProject.ObjectPool/ITimedObjectPool.cs
+++ b/src/CodeProject.ObjectPool/ITimedObjectPool.cs
@@ -21,6 +21,8 @@
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+using System;
+
namespace CodeProject.ObjectPool
{
///
@@ -38,5 +40,10 @@ public interface ITimedObjectPool : IObjectPool
#endif
where T : PooledObject
{
+ ///
+ /// When pooled objects have not been used for a time greater than ,
+ /// then they will be destroyed by a cleaning task.
+ ///
+ TimeSpan Timeout { get; set; }
}
}
\ No newline at end of file
diff --git a/src/CodeProject.ObjectPool/ObjectPool.cs b/src/CodeProject.ObjectPool/ObjectPool.cs
index a5b4b64..96b6050 100644
--- a/src/CodeProject.ObjectPool/ObjectPool.cs
+++ b/src/CodeProject.ObjectPool/ObjectPool.cs
@@ -37,55 +37,6 @@ public static class ObjectPool
public class ObjectPool : IObjectPool, IObjectPoolHandle
where T : PooledObject
{
- #region Public Properties
-
- ///
- /// Gets the Diagnostics class for the current Object Pool, whose goal is to record data
- /// about how the pool operates. By default, however, an object pool records anything; you
- /// have to enable it through the property.
- ///
- public ObjectPoolDiagnostics Diagnostics { get; set; }
-
- ///
- /// Gets the Factory method that will be used for creating new objects.
- ///
- public Func FactoryMethod { get; protected set; }
-
- ///
- /// Gets or sets the maximum number of objects that could be available at the same time in
- /// the pool.
- ///
- public int MaximumPoolSize
- {
- get
- {
- return PooledObjects.Capacity;
- }
- set
- {
- // Preconditions
- Raise.ArgumentOutOfRangeException.If(value < 1, nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize);
-
- // Resize the pool and destroy exceeding items, if any.
- foreach (var exceedingItem in PooledObjects.Resize(value))
- {
- DestroyPooledObject(exceedingItem);
- }
- }
- }
-
- ///
- /// Gets the count of the objects currently in the pool.
- ///
- public int ObjectsInPoolCount => PooledObjects.Count;
-
- ///
- /// The concurrent buffer containing pooled objects.
- ///
- protected PooledObjectBuffer PooledObjects { get; } = new PooledObjectBuffer();
-
- #endregion Public Properties
-
#region C'tor and Initialization code
///
@@ -99,7 +50,7 @@ public ObjectPool()
///
/// Initializes a new pool with specified maximum pool size.
///
- /// The maximum pool size limit
+ /// The maximum pool size limit.
///
/// is less than or equal to zero.
///
@@ -120,7 +71,7 @@ public ObjectPool(Func factoryMethod)
///
/// Initializes a new pool with specified factory method and maximum size.
///
- /// The maximum pool size limit
+ /// The maximum pool size limit.
/// The factory method that will be used to create new objects.
///
/// is less than or equal to zero.
@@ -128,10 +79,14 @@ public ObjectPool(Func factoryMethod)
public ObjectPool(int maximumPoolSize, Func factoryMethod)
{
// Preconditions
- Raise.ArgumentOutOfRangeException.If(maximumPoolSize < 1, nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize);
+ Raise.ArgumentOutOfRangeException.IfIsLessOrEqual(maximumPoolSize, 0, nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize);
+
+ // Throws an exception if the type does not have default constructor - on purpose! We
+ // could have added a generic constraint with new (), but we did not want to limit the
+ // user and force a parameterless constructor.
+ FactoryMethod = factoryMethod ?? Activator.CreateInstance;
- // Assigning properties.
- FactoryMethod = factoryMethod;
+ // Max pool size.
MaximumPoolSize = maximumPoolSize;
// Creating a new instance for the Diagnostics class.
@@ -140,6 +95,55 @@ public ObjectPool(int maximumPoolSize, Func factoryMethod)
#endregion C'tor and Initialization code
+ #region Public Properties
+
+ ///
+ /// Gets the Diagnostics class for the current Object Pool, whose goal is to record data
+ /// about how the pool operates. By default, however, an object pool records anything; you
+ /// have to enable it through the property.
+ ///
+ public ObjectPoolDiagnostics Diagnostics { get; set; }
+
+ ///
+ /// Gets the Factory method that will be used for creating new objects.
+ ///
+ public Func FactoryMethod { get; protected set; }
+
+ ///
+ /// Gets or sets the maximum number of objects that could be available at the same time in
+ /// the pool.
+ ///
+ public int MaximumPoolSize
+ {
+ get
+ {
+ return PooledObjects.Capacity;
+ }
+ set
+ {
+ // Preconditions
+ Raise.ArgumentOutOfRangeException.If(value < 1, nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize);
+
+ // Resize the pool and destroy exceeding items, if any.
+ foreach (var exceedingItem in PooledObjects.Resize(value))
+ {
+ DestroyPooledObject(exceedingItem);
+ }
+ }
+ }
+
+ ///
+ /// Gets the count of the objects currently in the pool.
+ ///
+ public int ObjectsInPoolCount => PooledObjects.Count;
+
+ ///
+ /// The concurrent buffer containing pooled objects.
+ ///
+ protected PooledObjectBuffer PooledObjects { get; } = new PooledObjectBuffer();
+
+ #endregion Public Properties
+
#region Finalizer
///
@@ -245,33 +249,45 @@ 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 virtual T CreatePooledObject()
{
if (Diagnostics.Enabled)
{
Diagnostics.IncrementObjectsCreatedCount();
}
- // Throws an exception if the type does not have default constructor - on purpose! We
- // could have added a generic constraint with new (), but we did not want to limit the
- // user and force a parameterless constructor.
- var newObject = FactoryMethod?.Invoke() ?? Activator.CreateInstance();
+ if (FactoryMethod == null)
+ {
+ // A child class has deleted our factory method. Therefore, we can only return null.
+ return null;
+ }
+
+ var newObject = FactoryMethod();
// Setting the 'return to pool' action and other properties in the newly created pooled object.
newObject.PooledObjectInfo.Id = Interlocked.Increment(ref _lastPooledObjectId);
newObject.PooledObjectInfo.State = PooledObjectState.Available;
newObject.PooledObjectInfo.Handle = this;
+
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 +309,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/ParameterizedObjectPool.cs b/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs
index 1b9ee4a..52b4f3d 100644
--- a/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs
+++ b/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs
@@ -100,7 +100,7 @@ public ParameterizedObjectPool()
///
/// Initializes a new pool with specified maximum pool size.
///
- /// The maximum pool size limit
+ /// The maximum pool size limit.
public ParameterizedObjectPool(int maximumPoolSize)
: this(maximumPoolSize, null)
{
@@ -118,7 +118,7 @@ public ParameterizedObjectPool(Func factoryMethod)
///
/// Initializes a new pool with specified factory method and maximum size.
///
- /// The maximum pool size limit
+ /// The maximum pool size limit.
/// The factory method that will be used to create new objects.
public ParameterizedObjectPool(int maximumPoolSize, Func factoryMethod)
{
diff --git a/src/CodeProject.ObjectPool/PooledObject.cs b/src/CodeProject.ObjectPool/PooledObject.cs
index 624ae29..1ceb2b4 100644
--- a/src/CodeProject.ObjectPool/PooledObject.cs
+++ b/src/CodeProject.ObjectPool/PooledObject.cs
@@ -55,21 +55,24 @@ internal bool ReleaseResources()
{
var successFlag = true;
- try
- {
- OnReleaseResources();
- }
- catch (Exception ex)
+ if (OnReleaseResources != null)
{
-#if !NET35
- if (Log.IsWarnEnabled())
+ try
{
- Log.WarnException("[ObjectPool] An unexpected error occurred while releasing resources", ex);
+ OnReleaseResources();
}
+ catch (Exception ex)
+ {
+#if !NET35
+ if (Log.IsWarnEnabled())
+ {
+ Log.WarnException("[ObjectPool] An unexpected error occurred while releasing resources", ex);
+ }
#else
- System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings
+ System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings
#endif
- successFlag = false;
+ successFlag = false;
+ }
}
return successFlag;
@@ -83,33 +86,36 @@ internal bool ResetState()
{
var successFlag = true;
- try
- {
- OnResetState();
- }
- catch (CannotResetStateException crsex)
+ if (OnResetState != null)
{
-#if !NET35
- if (Log.IsDebugEnabled())
+ try
{
- Log.DebugException("[ObjectPool] Object state could not be reset", crsex);
+ OnResetState();
}
+ catch (CannotResetStateException crsex)
+ {
+#if !NET35
+ if (Log.IsDebugEnabled())
+ {
+ Log.DebugException("[ObjectPool] Object state could not be reset", crsex);
+ }
#else
- System.Diagnostics.Debug.Assert(crsex != null); // Placeholder to avoid warnings
+ System.Diagnostics.Debug.Assert(crsex != null); // Placeholder to avoid warnings
#endif
- successFlag = false;
- }
- catch (Exception ex)
- {
-#if !NET35
- if (Log.IsWarnEnabled())
- {
- Log.WarnException("[ObjectPool] An unexpected error occurred while resetting state", ex);
+ successFlag = false;
}
+ catch (Exception ex)
+ {
+#if !NET35
+ if (Log.IsWarnEnabled())
+ {
+ Log.WarnException("[ObjectPool] An unexpected error occurred while resetting state", ex);
+ }
#else
- System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings
+ System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings
#endif
- successFlag = false;
+ successFlag = false;
+ }
}
return successFlag;
@@ -117,23 +123,19 @@ internal bool ResetState()
#endregion Internal Methods - resource and state management
- #region Virtual Template Methods - extending resource and state management
+ #region Events - extending resource and state management
///
/// Reset the object state to allow this object to be re-used by other parts of the application.
///
- protected virtual void OnResetState()
- {
- }
+ public Action OnResetState { get; set; }
///
/// Releases the object's resources.
///
- protected virtual void OnReleaseResources()
- {
- }
+ public Action OnReleaseResources { get; set; }
- #endregion Virtual Template Methods - extending resource and state management
+ #endregion Events - extending resource and state management
#region Returning object to pool - Dispose and Finalizer
@@ -199,6 +201,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/PooledObjectWrapper.cs b/src/CodeProject.ObjectPool/PooledObjectWrapper.cs
index 48897d3..9c1ce86 100644
--- a/src/CodeProject.ObjectPool/PooledObjectWrapper.cs
+++ b/src/CodeProject.ObjectPool/PooledObjectWrapper.cs
@@ -31,17 +31,10 @@ public PooledObjectWrapper(T resource)
Raise.ArgumentNullException.IfIsNull(resource, nameof(resource), ErrorMessages.NullResource);
InternalResource = resource;
- }
-
- ///
- /// Triggered by the pool manager when there is no need for this object anymore.
- ///
- public Action WrapperReleaseResourcesAction { get; set; }
- ///
- /// Triggered by the pool manager just before the object is being returned to the pool.
- ///
- public Action WrapperResetStateAction { get; set; }
+ base.OnReleaseResources += () => OnReleaseResources?.Invoke(InternalResource);
+ base.OnResetState += () => OnResetState?.Invoke(InternalResource);
+ }
///
/// The resource wrapped inside this class.
@@ -49,19 +42,13 @@ public PooledObjectWrapper(T resource)
public T InternalResource { get; }
///
- /// Triggers the , if any.
+ /// Triggered by the pool manager when there is no need for this object anymore.
///
- protected override void OnReleaseResources()
- {
- WrapperReleaseResourcesAction?.Invoke(InternalResource);
- }
+ public new Action OnReleaseResources { get; set; }
///
- /// Triggers the , if any.
+ /// Triggered by the pool manager just before the object is being returned to the pool.
///
- protected override void OnResetState()
- {
- WrapperResetStateAction?.Invoke(InternalResource);
- }
+ public new Action OnResetState { get; set; }
}
}
\ No newline at end of file
diff --git a/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs b/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs
index 1b26ec9..bde80d6 100644
--- a/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs
+++ b/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs
@@ -47,6 +47,33 @@ public PooledMemoryStream(int capacity)
{
Parent = this
};
+
+ OnResetState += () =>
+ {
+ if (!_trackedMemoryStream.CanRead || !_trackedMemoryStream.CanWrite || !_trackedMemoryStream.CanSeek)
+ {
+ throw new CannotResetStateException($"Memory stream has already been disposed");
+ }
+
+ var memoryStreamPool = PooledObjectInfo.Handle as IMemoryStreamPool;
+ if (_trackedMemoryStream.Capacity < memoryStreamPool.MinimumMemoryStreamCapacity)
+ {
+ throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while minimum required capacity is {memoryStreamPool.MinimumMemoryStreamCapacity}");
+ }
+ if (_trackedMemoryStream.Capacity > memoryStreamPool.MaximumMemoryStreamCapacity)
+ {
+ throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while maximum allowed capacity is {memoryStreamPool.MaximumMemoryStreamCapacity}");
+ }
+
+ _trackedMemoryStream.Position = 0L;
+ _trackedMemoryStream.SetLength(0L);
+ };
+
+ OnReleaseResources += () =>
+ {
+ _trackedMemoryStream.Parent = null;
+ _trackedMemoryStream.Dispose();
+ };
}
///
@@ -60,41 +87,6 @@ public PooledMemoryStream(int capacity)
/// A string that represents the current object.
public override string ToString() => _trackedMemoryStream.ToString();
- ///
- /// Reset the object state to allow this object to be re-used by other parts of the application.
- ///
- protected override void OnResetState()
- {
- if (!_trackedMemoryStream.CanRead || !_trackedMemoryStream.CanWrite || !_trackedMemoryStream.CanSeek)
- {
- throw new CannotResetStateException($"Memory stream has already been disposed");
- }
-
- var memoryStreamPool = PooledObjectInfo.Handle as IMemoryStreamPool;
- if (_trackedMemoryStream.Capacity < memoryStreamPool.MinimumMemoryStreamCapacity)
- {
- throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while minimum required capacity is {memoryStreamPool.MinimumMemoryStreamCapacity}");
- }
- if (_trackedMemoryStream.Capacity > memoryStreamPool.MaximumMemoryStreamCapacity)
- {
- throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while maximum allowed capacity is {memoryStreamPool.MaximumMemoryStreamCapacity}");
- }
-
- _trackedMemoryStream.Position = 0L;
- _trackedMemoryStream.SetLength(0L);
- base.OnResetState();
- }
-
- ///
- /// Releases the object's resources.
- ///
- protected override void OnReleaseResources()
- {
- _trackedMemoryStream.Parent = null;
- _trackedMemoryStream.Dispose();
- base.OnReleaseResources();
- }
-
private sealed class TrackedMemoryStream : MemoryStream
{
public TrackedMemoryStream(int capacity)
diff --git a/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs b/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs
index 6c43ac6..755fbb6 100644
--- a/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs
+++ b/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs
@@ -44,37 +44,29 @@ public class PooledStringBuilder : PooledObject
public PooledStringBuilder(int capacity)
{
StringBuilder = new StringBuilder(capacity);
- }
- ///
- /// Returns a string that represents the current object.
- ///
- /// A string that represents the current object.
- public override string ToString() => StringBuilder.ToString();
-
- ///
- /// Reset the object state to allow this object to be re-used by other parts of the application.
- ///
- protected override void OnResetState()
- {
- var stringBuilderPool = PooledObjectInfo.Handle as IStringBuilderPool;
- if (StringBuilder.Capacity > stringBuilderPool.MaximumStringBuilderCapacity)
+ OnResetState += () =>
{
- throw new CannotResetStateException($"String builder capacity is {StringBuilder.Capacity}, while maximum allowed capacity is {stringBuilderPool.MaximumStringBuilderCapacity}");
- }
+ var stringBuilderPool = PooledObjectInfo.Handle as IStringBuilderPool;
+ if (StringBuilder.Capacity > stringBuilderPool.MaximumStringBuilderCapacity)
+ {
+ throw new CannotResetStateException($"String builder capacity is {StringBuilder.Capacity}, while maximum allowed capacity is {stringBuilderPool.MaximumStringBuilderCapacity}");
+ }
+
+ ClearStringBuilder();
+ };
- ClearStringBuilder();
- base.OnResetState();
+ OnReleaseResources += () =>
+ {
+ ClearStringBuilder();
+ };
}
///
- /// Releases the object's resources.
+ /// Returns a string that represents the current object.
///
- protected override void OnReleaseResources()
- {
- ClearStringBuilder();
- base.OnReleaseResources();
- }
+ /// A string that represents the current object.
+ public override string ToString() => StringBuilder.ToString();
///
/// Clears the property, using specific methods depending on
diff --git a/src/CodeProject.ObjectPool/TimedObjectPool.cs b/src/CodeProject.ObjectPool/TimedObjectPool.cs
index e74fd7b..70b944c 100644
--- a/src/CodeProject.ObjectPool/TimedObjectPool.cs
+++ b/src/CodeProject.ObjectPool/TimedObjectPool.cs
@@ -23,6 +23,10 @@
#if !(NETSTD10 || NETSTD11)
+using CodeProject.ObjectPool.Core;
+using PommaLabs.Thrower;
+using System;
+using System.Linq;
using System.Threading;
namespace CodeProject.ObjectPool
@@ -34,10 +38,156 @@ namespace CodeProject.ObjectPool
/// The type of the object that which will be managed by the pool. The pooled object have to be
/// a sub-class of PooledObject.
///
- internal class TimedObjectPool : ObjectPool, ITimedObjectPool
+ public class TimedObjectPool : ObjectPool, ITimedObjectPool
where T : PooledObject
{
- private readonly Timer Timer;
+ #region Fields
+
+ ///
+ /// Backing field for .
+ ///
+ private TimeSpan _timeout;
+
+ ///
+ /// The timer which periodically cleans the pool up.
+ ///
+ private Timer _timer;
+
+ #endregion Fields
+
+ #region C'tor and Initialization code
+
+ ///
+ /// Initializes a new timed pool with default settings and specified timeout.
+ ///
+ /// The timeout of each pooled object.
+ ///
+ /// is less than or equal to .
+ ///
+ public TimedObjectPool(TimeSpan timeout)
+ : this(ObjectPool.DefaultPoolMaximumSize, null, timeout)
+ {
+ }
+
+ ///
+ /// Initializes a new timed pool with specified maximum pool size and timeout.
+ ///
+ /// The maximum pool size limit.
+ /// The timeout of each pooled object.
+ ///
+ /// is less than or equal to zero.
+ /// is less than or equal to .
+ ///
+ public TimedObjectPool(int maximumPoolSize, TimeSpan timeout)
+ : this(maximumPoolSize, null, timeout)
+ {
+ }
+
+ ///
+ /// Initializes a new timed pool with specified factory method and timeout.
+ ///
+ /// The factory method that will be used to create new objects.
+ /// The timeout of each pooled object.
+ ///
+ /// is less than or equal to .
+ ///
+ public TimedObjectPool(Func factoryMethod, TimeSpan timeout)
+ : this(ObjectPool.DefaultPoolMaximumSize, factoryMethod, timeout)
+ {
+ }
+
+ ///
+ /// Initializes a new timed pool with specified factory method, maximum size and timeout.
+ ///
+ /// The maximum pool size limit.
+ /// The factory method that will be used to create new objects.
+ /// The timeout of each pooled object.
+ ///
+ /// is less than or equal to zero.
+ /// is less than or equal to .
+ ///
+ public TimedObjectPool(int maximumPoolSize, Func factoryMethod, TimeSpan timeout) : base(maximumPoolSize, factoryMethod)
+ {
+ // Preconditions
+ Raise.ArgumentOutOfRangeException.IfIsLessOrEqual(timeout, TimeSpan.Zero, nameof(timeout), ErrorMessages.NegativeOrZeroTimeout);
+
+ // Assigning properties.
+ Timeout = timeout;
+ }
+
+ #endregion C'tor and Initialization code
+
+ #region Public Properties
+
+ ///
+ /// 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();
+ }
+ }
+
+ #endregion Public Properties
+
+ #region Core Methods
+
+ ///
+ /// Creates a new pooled object, initializing its info.
+ ///
+ /// A new pooled object.
+ protected override T CreatePooledObject()
+ {
+ var pooledObject = base.CreatePooledObject();
+
+ // Register an handler which records the time at which the object returned to the pool.
+ pooledObject.OnResetState += () =>
+ {
+ pooledObject.PooledObjectInfo.Payload = DateTime.UtcNow;
+ };
+
+ return pooledObject;
+ }
+
+ ///
+ /// 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);
+ }
+ }
+
+ #endregion Core Methods
}
}
diff --git a/src/CodeProject.ObjectPool/doxyfile.txt b/src/CodeProject.ObjectPool/doxyfile.txt
index d4a7c9a..4a2c4e3 100644
--- a/src/CodeProject.ObjectPool/doxyfile.txt
+++ b/src/CodeProject.ObjectPool/doxyfile.txt
@@ -38,7 +38,7 @@ PROJECT_NAME = "Generic and concurrent Object Pool"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 3.0.2
+PROJECT_NUMBER = 3.0.3
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/test/CodeProject.ObjectPool.Examples/Program.cs b/test/CodeProject.ObjectPool.Examples/Program.cs
index 877730e..9f50dc5 100644
--- a/test/CodeProject.ObjectPool.Examples/Program.cs
+++ b/test/CodeProject.ObjectPool.Examples/Program.cs
@@ -21,6 +21,9 @@
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+using System;
+using System.Threading;
+
namespace CodeProject.ObjectPool.Examples
{
///
@@ -48,8 +51,8 @@ private static void Main()
var newPool = new ObjectPool>(() =>
new PooledObjectWrapper(CreateNewResource())
{
- WrapperReleaseResourcesAction = r => ExternalResourceReleaseResource(r),
- WrapperResetStateAction = r => ExternalResourceResetState(r)
+ OnReleaseResources = ExternalResourceReleaseResource,
+ OnResetState = ExternalResourceResetState
});
using (var wrapper = newPool.GetObject())
@@ -57,6 +60,22 @@ private static void Main()
// wrapper.InternalResource contains the object that you pooled.
wrapper.InternalResource.DoOtherStuff();
} // Exiting the using scope will return the object back to the pool.
+
+ // Creates a pool where objects which have not been used for over 2 seconds will be
+ // cleaned up by a dedicated thread.
+ var timedPool = new TimedObjectPool(TimeSpan.FromSeconds(2));
+
+ using (var resource = timedPool.GetObject())
+ {
+ // Using the resource...
+ resource.DoStuff();
+ } // Exiting the using scope will return the object back to the pool and record last usage.
+
+ Console.WriteLine($"Timed pool size after 0 seconds: {timedPool.ObjectsInPoolCount}"); // Should be 1
+ Thread.Sleep(TimeSpan.FromSeconds(4));
+ Console.WriteLine($"Timed pool size after 4 seconds: {timedPool.ObjectsInPoolCount}"); // Should be 0
+
+ Console.Read();
}
private static ExternalExpensiveResource CreateNewResource()
@@ -77,19 +96,22 @@ public static void ExternalResourceReleaseResource(ExternalExpensiveResource res
internal sealed class ExpensiveResource : PooledObject
{
- public void DoStuff()
+ public ExpensiveResource()
{
- // Do some work here, for example.
- }
+ OnReleaseResources = () =>
+ {
+ // Called if the resource needs to be manually cleaned before the memory is reclaimed.
+ };
- protected override void OnReleaseResources()
- {
- // Override if the resource needs to be manually cleaned before the memory is reclaimed.
+ OnResetState = () =>
+ {
+ // Called if the resource needs resetting before it is getting back into the pool.
+ };
}
- protected override void OnResetState()
+ public void DoStuff()
{
- // Override if the resource needs resetting before it is getting back into the pool.
+ // Do some work here, for example.
}
}
diff --git a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj
index e0b1b67..b1e1762 100644
--- a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj
+++ b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj
@@ -2,8 +2,7 @@
CodeProject.ObjectPool.UnitTests
CodeProject.ObjectPool.UnitTests
-
- net35;net40;net45;net46
+ netcoreapp1.1;net35;net40;net45;net46
1.1.1
Exe
false
@@ -16,20 +15,21 @@
-
-
+
+
+
-
+
-
+
+
$(DefineConstants);NET35