Skip to content

Commit

Permalink
Implement tests for reinitialise error event, fix bugs.
Browse files Browse the repository at this point in the history
Remove unused code and clean up new code analysis warnings.
  • Loading branch information
Yortw committed Jan 17, 2021
1 parent acc4f5e commit 4f47b1d
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 62 deletions.
Binary file modified src/PoolSharp.Net40/GlobalSuppressions.cs
Binary file not shown.
86 changes: 25 additions & 61 deletions src/PoolSharp.Shared.ConcurrentBagImplementation/Pool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class Pool<T> : PoolBase<T>
private long _PoolInstancesCount;

#if SUPPORTS_THREADS
System.Threading.Thread _ReinitialiseThread;
private readonly System.Threading.Thread _ReinitialiseThread;
#endif

#endregion
Expand All @@ -53,9 +53,11 @@ public Pool(PoolPolicy<T> poolPolicy) : base(poolPolicy)
{
_ItemsToInitialise = new System.Collections.Concurrent.BlockingCollection<T>();
#if SUPPORTS_THREADS
_ReinitialiseThread = new System.Threading.Thread(this.BackgroundReinitialise);
_ReinitialiseThread.Name = this.GetType().FullName + " Background Reinitialise";
_ReinitialiseThread.IsBackground = true;
_ReinitialiseThread = new System.Threading.Thread(this.BackgroundReinitialise)
{
Name = this.GetType().FullName + " Background Reinitialise",
IsBackground = true
};
_ReinitialiseThread.Start();
#else
System.Threading.Tasks.Task.Factory.StartNew(this.BackgroundReinitialise, System.Threading.Tasks.TaskCreationOptions.LongRunning);
Expand All @@ -80,14 +82,16 @@ public override T Take()
{
CheckDisposed();

T retVal;

if (_Pool.TryTake(out retVal))
if (_Pool.TryTake(out var retVal))
{
Interlocked.Decrement(ref _PoolInstancesCount);

if (PoolPolicy.InitializationPolicy == PooledItemInitialization.Take && PoolPolicy.ReinitializeObject != null)
PoolPolicy.ReinitializeObject(retVal);
{
if (!ReinitialiseObject(retVal))
retVal = PoolPolicy.Factory(this);
}
}
else
retVal = PoolPolicy.Factory(this);
Expand Down Expand Up @@ -125,7 +129,9 @@ public override void Add(T value)
else
{
if (PoolPolicy.InitializationPolicy == PooledItemInitialization.Return && PoolPolicy.ReinitializeObject != null)
PoolPolicy.ReinitializeObject(value);
{
if (!ReinitialiseObject(value)) return;
}

_Pool.Add(value);
Interlocked.Increment(ref _PoolInstancesCount);
Expand Down Expand Up @@ -190,11 +196,9 @@ protected override void Dispose(bool disposing)

if (IsPooledTypeDisposable)
{
T item;

while (!_Pool.IsEmpty)
{
_Pool.TryTake(out item);
_Pool.TryTake(out var item);
SafeDispose(item);
}
}
Expand All @@ -207,7 +211,7 @@ protected override void Dispose(bool disposing)

private void BackgroundReinitialise()
{
T item = default(T);
T item = default;
while (!_ItemsToInitialise.IsCompleted)
{
try
Expand All @@ -225,19 +229,7 @@ private void BackgroundReinitialise()
SafeDispose(item);
else
{
try
{
if (PoolPolicy.ReinitializeObject != null)
PoolPolicy.ReinitializeObject(item);
}
catch (Exception ex)
{
OnReinitialiseError(new ReinitialiseErrorEventArgs<T>(ex, item));
SafeDispose(item);
item = default;
}

if (item != null && ShouldReturnToPool(item))
if (ReinitialiseObject(item) && ShouldReturnToPool(item))
{
_Pool.Add(item);

Expand All @@ -258,47 +250,19 @@ private void SafeAddToReinitialiseQueue(T pooledObject)
catch (InvalidOperationException) { } //Handle race condition on above if condition.
}

private void ProcessReturnedItems()
private bool ReinitialiseObject(T item)
{
//Only bother reinitialising items while we're alive.
//If we're shutdown, even with items left to process, then just ignore them.
//We're not going to use them anyway.
while (!_ItemsToInitialise.IsAddingCompleted)
{
ReinitialiseAndReturnToPoolOrDispose(_ItemsToInitialise.Take());
}

//If we're done but the there are disposable items in the queue,
//dispose each one.
if (!_ItemsToInitialise.IsCompleted && IsPooledTypeDisposable)
try
{
while (!_ItemsToInitialise.IsCompleted)
{
SafeDispose(_ItemsToInitialise.Take());
}
PoolPolicy.ReinitializeObject(item);
return true;
}
}

private void ReinitialiseAndReturnToPoolOrDispose(T value)
{
if (ShouldReturnToPool(value))
catch (Exception ex)
{
try
{
PoolPolicy.ReinitializeObject(value);
}
catch (Exception ex)
{
OnReinitialiseError(new ReinitialiseErrorEventArgs<T>(ex, value));
SafeDispose(value);
return;
}

_Pool.Add(value);
Interlocked.Increment(ref _PoolInstancesCount);
OnReinitialiseError(new ReinitialiseErrorEventArgs<T>(ex, item));
SafeDispose(item);
}
else
SafeDispose(value);
return false;
}

private bool ShouldReturnToPool(T pooledObject)
Expand Down
88 changes: 87 additions & 1 deletion src/PoolSharp.Tests/PoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void Pool_Take_ThrowsWhenPoolDisposed()
{
var pool = GetPool(1, PooledItemInitialization.Take);
pool.Dispose();
var item = pool.Take();
_ = pool.Take();
}

#endregion
Expand Down Expand Up @@ -367,6 +367,92 @@ public void PooledObject_Dispose_ReturnsToPool()

#endregion

#region Reinitialise Error Handling Tests

[TestMethod]
public void PooledObject_BackgroundReinitialise_RasisesEvent_OnReinitialiseError()
{
var policy = new PoolPolicy<PooledObject<DisposableTestPoolItem>>()
{
Factory = (p) => new PooledObject<DisposableTestPoolItem>(p, new DisposableTestPoolItem()),
InitializationPolicy = PooledItemInitialization.AsyncReturn,
MaximumPoolSize = 1,
ReinitializeObject = (item) =>
{
throw new OutOfMemoryException("Test");
}
};

var eventRaised = false;
using (var eventRaisedSignal = new System.Threading.ManualResetEvent(false))
{
var pool = new Pool<PooledObject<DisposableTestPoolItem>>(policy);
pool.ReinitialiseError += (s, e) => { eventRaised = true; eventRaisedSignal.Set(); };

var item = pool.Take();
pool.Add(item);
eventRaisedSignal.WaitOne(1000);
Assert.IsTrue(eventRaised);
}
}

[TestMethod]
public void PooledObject_ReinitialiseOnReturn_RasisesEvent_OnReinitialiseError()
{
var policy = new PoolPolicy<PooledObject<DisposableTestPoolItem>>()
{
Factory = (p) => new PooledObject<DisposableTestPoolItem>(p, new DisposableTestPoolItem()),
InitializationPolicy = PooledItemInitialization.Return,
MaximumPoolSize = 1,
ReinitializeObject = (item) =>
{
throw new OutOfMemoryException("Test");
}
};

var eventRaised = false;
using (var eventRaisedSignal = new System.Threading.ManualResetEvent(false))
{
var pool = new Pool<PooledObject<DisposableTestPoolItem>>(policy);
pool.ReinitialiseError += (s, e) => { eventRaised = true; eventRaisedSignal.Set(); };

var item = pool.Take();
pool.Add(item);
eventRaisedSignal.WaitOne(1000);
Assert.IsTrue(eventRaised);
}
}

[TestMethod]
public void PooledObject_ReinitialiseOnTake_RasisesEvent_OnReinitialiseError()
{
var policy = new PoolPolicy<PooledObject<DisposableTestPoolItem>>()
{
Factory = (p) => new PooledObject<DisposableTestPoolItem>(p, new DisposableTestPoolItem()),
InitializationPolicy = PooledItemInitialization.Take,
MaximumPoolSize = 1,
ReinitializeObject = (item) =>
{
throw new OutOfMemoryException("Test");
}
};

var eventRaised = false;
using (var eventRaisedSignal = new System.Threading.ManualResetEvent(false))
{
var pool = new Pool<PooledObject<DisposableTestPoolItem>>(policy);
pool.ReinitialiseError += (s, e) => { eventRaised = true; eventRaisedSignal.Set(); };

var item = pool.Take();
pool.Add(item);
item = pool.Take();
eventRaisedSignal.WaitOne(1000);
Assert.IsTrue(eventRaised);
}
}

#endregion

#region Private Methods

private IPool<TestPoolItem> GetPool()
Expand Down
Binary file modified src/PoolSharp.iOS/GlobalSuppressions.cs
Binary file not shown.

0 comments on commit 4f47b1d

Please sign in to comment.