#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Threading;
using NUnit.Framework;
using Rhino.Mocks;
using Spring.Pool.Support;
using Spring.Threading;
#endregion
namespace Spring.Pool{
#region Inner Class : Helper
public class Helper
{
private Latch latch;
private IObjectPool objectPool;
private ISync sync;
public bool gone = false;
public object gotFromPool;
public Helper(ISync sync, Latch latch)
{
Init(latch, sync, null);
}
public Helper(Latch latch, ISync sync, IObjectPool objectPool)
{
Init(latch, sync, objectPool);
}
public void Init(Latch latch, ISync sync, IObjectPool objectPool)
{
this.sync = sync;
this.latch = latch;
this.objectPool = objectPool;
}
public void Go()
{
latch.Release();
sync.Acquire();
gone = true;
sync.Release();
}
public void UsePool()
{
gotFromPool = objectPool.BorrowObject();
latch.Release();
}
}
#endregion
/// <summary>
/// Unit tests for the SimplePool class.
/// </summary>
[TestFixture]
public sealed class SimplePoolTests
{
#region Inner Class : MyFactory (IPoolableObjectFactory implementation)
private sealed class MyFactory : IPoolableObjectFactory
{
public object MakeObject()
{
return new object();
}
public void DestroyObject(object o)
{
}
public bool ValidateObject(object o)
{
return true;
}
public void ActivateObject(object o)
{
}
public void PassivateObject(object o)
{
}
}
#endregion
private MockRepository mocks;
private IPoolableObjectFactory factory;
private SimplePool pool;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
factory = (IPoolableObjectFactory) mocks.DynamicMock(typeof(IPoolableObjectFactory));
Expect.Call(factory.MakeObject()).Return(new object()).Repeat.Any();
mocks.ReplayAll();
pool = new SimplePool(factory, 1);
mocks.BackToRecordAll();
}
[Test]
[ExpectedException(typeof (ArgumentNullException))]
public void InstantiateWithNullPoolableObjectFactory()
{
new SimplePool(null, 10);
}
[Test]
[ExpectedException(typeof (ArgumentException))]
public void InstantiateSpecifyingZeroPooledItems()
{
new SimplePool(factory, 0);
}
[Test]
[ExpectedException(typeof (ArgumentException))]
public void InstantiateSpecifyingNegativePooledItems()
{
new SimplePool(factory, -10000);
}
[Test]
public void ActivateOnObjectOnBorrow()
{
Expect.Call(factory.ValidateObject(null)).IgnoreArguments().Return(true).Repeat.Any();
factory.ActivateObject(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
Assert.AreEqual(0, pool.NumActive, "active wrong");
Assert.AreEqual(1, pool.NumIdle, "idle wrong");
pool.BorrowObject();
Assert.AreEqual(1, pool.NumActive, "active wrong");
Assert.AreEqual(0, pool.NumIdle, "idle wrong");
mocks.VerifyAll();
}
// TODO fix test!!!
[Test]
[Ignore("Cannot figure out why this is failing?")]
public void PassivateBusyObjectsBeforeClose()
{
Expect.Call(factory.ValidateObject(null)).IgnoreArguments().Return(true).Repeat.Any();
object o = pool.BorrowObject();
factory.PassivateObject(o);
mocks.ReplayAll();
pool.Close();
mocks.VerifyAll();
}
[Test, ExpectedException(typeof (PoolException))]
public void NoMoreUsableAfterClose()
{
object o = pool.BorrowObject();
factory.PassivateObject(o);
mocks.ReplayAll();
pool.Close();
pool.BorrowObject();
mocks.VerifyAll();
}
[Test, ExpectedException(typeof (PoolException))]
public void ThrowsExceptionWhenOutOfItemsBecauseFailedValidation()
{
object o = new object();
Expect.Call(factory.MakeObject()).Return(o);
Expect.Call(factory.ValidateObject(o)).Return(false);
mocks.ReplayAll();
pool = new SimplePool(factory, 1);
pool.BorrowObject();
mocks.VerifyAll();
}
[Test]
public void PassivateObjectOnReturn()
{
Expect.Call(factory.ValidateObject(null)).IgnoreArguments().Return(true).Repeat.Any();
factory.PassivateObject(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
pool.ReturnObject(pool.BorrowObject());
mocks.VerifyAll();
}
[Test]
public void DestroyObjectOnClose()
{
Expect.Call(factory.ValidateObject(null)).IgnoreArguments().Return(true).Repeat.Any();
factory.DestroyObject(null);
LastCall.IgnoreArguments();
mocks.ReplayAll();
pool.BorrowObject();
pool.Close();
}
[Test]
public void WaitOnBorrowWhenExausted()
{
int n = 100;
object[] objects = new object[n];
pool = new SimplePool(new MyFactory(), n);
for (int i = 0; i < n; i++)
{
objects[i] = pool.BorrowObject();
}
Latch latch = new Latch();
ISync sync = new Latch();
Helper helper = new Helper(latch, sync, pool);
Thread thread = new Thread(new ThreadStart(helper.UsePool));
thread.Start();
Assert.AreEqual(n, pool.NumActive);
Assert.AreEqual(0, pool.NumIdle);
object released = objects[n - 1];
pool.ReturnObject(released);
latch.Acquire();
Assert.AreEqual(n, pool.NumActive);
Assert.AreEqual(0, pool.NumIdle);
Assert.IsNotNull(helper.gotFromPool, "helper did not get from pool");
Assert.AreSame(released, helper.gotFromPool, "got unexpected object");
}
}
[TestFixture]
public sealed class StessSimplePool
{
public sealed class QueryPerformance
{
public interface IQueried
{
void Run();
}
[DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
[DllImport("KERNEL32")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);
public static float Query(IQueried queried)
{
long frequency = 0;
QueryPerformanceFrequency(ref frequency);
long startTime = 0;
QueryPerformanceCounter(ref startTime);
queried.Run();
long endTime = 0;
QueryPerformanceCounter(ref endTime);
float elapsed = (float) (endTime - startTime)/frequency;
// Console.WriteLine("{0:0000.000}ms ", elapsed);
// Console.ReadLine();
return elapsed;
}
}
private sealed class Pooled : IPoolableObjectFactory, QueryPerformance.IQueried
{
private int creationTime;
private int executionTime;
public Pooled(int creationTime, int executionTime)
{
this.creationTime = creationTime;
this.executionTime = executionTime;
Thread.Sleep(creationTime);
}
public void Run()
{
Thread.Sleep(executionTime);
}
public object MakeObject()
{
return new Pooled(creationTime, executionTime);
}
public void DestroyObject(object o)
{
}
public bool ValidateObject(object o)
{
return true;
}
public void ActivateObject(object o)
{
}
public void PassivateObject(object o)
{
}
}
private sealed class Client : QueryPerformance.IQueried
{
private int repeat;
private static int nRun = 0, created = 0;
private int id;
private ISync run;
private SimplePool pool;
public bool runned;
public Client(int id, ISync run, SimplePool pool, int repeat)
{
this.run = run;
this.pool = pool;
this.id = id;
lock (this.GetType())
{
created++;
//Console.Out.WriteLine("#{0} created (created/run: {1}/{2})...", id, created, nRun);
}
this.repeat = repeat;
}
public void Run()
{
lock (this.GetType())
{
//Console.Out.WriteLine("#{0} running (created/run: {1}/{2})...", id, created, nRun);
nRun++;
}
for (int i = 0; i < repeat; i++)
{
QueryPerformance.IQueried queried = pool.BorrowObject() as QueryPerformance.IQueried;
queried.Run();
pool.ReturnObject(queried);
runned = true;
}
run.Release();
}
}
private sealed class Job : QueryPerformance.IQueried
{
private int repeat;
private IList clients = new ArrayList();
private int poolSize;
private int creationTime;
private int clientSize;
private ISync run;
private int executionTime;
public Job(int size, int creationTime, int clientSize, ISync start, int executionTime, int repeat)
{
this.poolSize = size;
this.creationTime = creationTime;
this.clientSize = clientSize;
this.run = start;
this.executionTime = executionTime;
this.repeat = repeat;
}
public void Run()
{
SimplePool pool = new SimplePool(new Pooled(creationTime, executionTime), poolSize);
for (int i = 0; i < clientSize; i++)
{
Client client = new Client(i, run, pool, repeat);
Thread thread = new Thread(new ThreadStart(client.Run));
thread.Start();
clients.Add(client);
}
run.Acquire();
foreach (Client client in clients)
Assert.IsTrue(client.runned, "\nclient " + clients.IndexOf(client) + " not runned");
}
public static string Do(int poolSize, int clientSize, int executionTime, int repeat, int creationTime)
{
ISync start = new Spring.Threading.Semaphore(-(clientSize - 1));
Job job = new Job(poolSize, creationTime, clientSize, start, executionTime, repeat);
float elapsed = QueryPerformance.Query(job);
return String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5:0.000} ",
creationTime, executionTime, poolSize, clientSize, repeat, elapsed);
}
}
[Test, Ignore("a try for a stress")]
public void ScalingAgainstIncreasingNumberOfThreads()
{
Console.Out.WriteLine("creationTime\texecutionTime\tpoolSize\tclientSize\trepeat\telapsed");
Console.Out.WriteLine("ramp pools size");
Console.Out.WriteLine(Job.Do(10, 1000, 100, 1, 1));
Console.Out.WriteLine(Job.Do(100, 1000, 100, 1, 1));
Console.Out.WriteLine(Job.Do(200, 1000, 100, 1, 1));
Console.Out.WriteLine(Job.Do(500, 1000, 100, 1, 1));
Console.Out.WriteLine(Job.Do(1000, 1000, 100, 1, 1));
Console.Out.WriteLine("ramp client size");
Console.Out.WriteLine(Job.Do(10, 10, 100, 1, 1));
Console.Out.WriteLine(Job.Do(10, 100, 100, 1, 1));
Console.Out.WriteLine(Job.Do(10, 200, 100, 1, 1));
Console.Out.WriteLine(Job.Do(10, 1000, 100, 1, 1));
Console.Out.WriteLine("ramp load");
Console.Out.WriteLine(Job.Do(10, 10, 100, 1, 1));
Console.Out.WriteLine(Job.Do(10, 10, 100, 5, 1));
Console.Out.WriteLine(Job.Do(10, 10, 100, 10, 1));
Console.Out.WriteLine(Job.Do(10, 10, 100, 50, 1));
Console.Out.WriteLine(Job.Do(10, 10, 100, 100, 1));
Console.Out.WriteLine("ramp client size - 2");
Console.Out.WriteLine(Job.Do(10, 1, 100, 1, 1));
Console.Out.WriteLine(Job.Do(10, 5, 100, 5, 1));
Console.Out.WriteLine(Job.Do(10, 10, 100, 10, 1));
Console.Out.WriteLine(Job.Do(10, 50, 100, 50, 1));
Console.Out.WriteLine(Job.Do(10, 100, 100, 100, 1));
Console.Out.WriteLine(Job.Do(10, 1000, 100, 100, 1));
}
}
}
|