blob: 5e3f9327b8c0ca13076721ca0087c9d7d0ba0473 [file] [log] [blame]
using J2N.Threading;
using NUnit.Framework;
using System;
using Assert = Lucene.Net.TestFramework.Assert;
namespace Lucene.Net.Util
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
[TestFixture]
public class TestDoubleBarrelLRUCache : LuceneTestCase
{
private void TestCache(DoubleBarrelLRUCache<CloneableInteger, object> cache, int n)
{
object dummy = new object();
for (int i = 0; i < n; i++)
{
cache.Put(new CloneableInteger(i), dummy);
}
// access every 2nd item in cache
for (int i = 0; i < n; i += 2)
{
Assert.IsNotNull(cache.Get(new CloneableInteger(i)));
}
// add n/2 elements to cache, the ones that weren't
// touched in the previous loop should now be thrown away
for (int i = n; i < n + (n / 2); i++)
{
cache.Put(new CloneableInteger(i), dummy);
}
// access every 4th item in cache
for (int i = 0; i < n; i += 4)
{
Assert.IsNotNull(cache.Get(new CloneableInteger(i)));
}
// add 3/4n elements to cache, the ones that weren't
// touched in the previous loops should now be thrown away
for (int i = n; i < n + (n * 3 / 4); i++)
{
cache.Put(new CloneableInteger(i), dummy);
}
// access every 4th item in cache
for (int i = 0; i < n; i += 4)
{
Assert.IsNotNull(cache.Get(new CloneableInteger(i)));
}
}
[Test]
public virtual void TestLRUCache()
{
const int n = 100;
TestCache(new DoubleBarrelLRUCache<CloneableInteger, object>(n), n);
}
private class CacheThread : ThreadJob
{
private readonly TestDoubleBarrelLRUCache outerInstance;
private readonly CloneableObject[] objs;
private readonly DoubleBarrelLRUCache<CloneableObject, object> c;
private readonly long endTime;
internal volatile bool failed;
public CacheThread(TestDoubleBarrelLRUCache outerInstance, DoubleBarrelLRUCache<CloneableObject, object> c, CloneableObject[] objs, long endTime)
{
this.outerInstance = outerInstance;
this.c = c;
this.objs = objs;
this.endTime = endTime;
}
public override void Run()
{
try
{
long count = 0;
long miss = 0;
long hit = 0;
int limit = objs.Length;
while (true)
{
CloneableObject obj = objs[(int)((count / 2) % limit)];
object v = c.Get(obj);
if (v is null)
{
c.Put(new CloneableObject(obj), obj);
miss++;
}
else
{
Assert.True(obj == v);
hit++;
}
if ((++count % 10000) == 0)
{
if (J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond > endTime) // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results
{
break;
}
}
}
outerInstance.AddResults(miss, hit);
}
catch (Exception t) when (t.IsThrowable())
{
failed = true;
throw RuntimeException.Create(t);
}
}
}
internal long totMiss, totHit;
internal virtual void AddResults(long miss, long hit)
{
totMiss += miss;
totHit += hit;
}
[Test]
public virtual void TestThreadCorrectness()
{
const int NUM_THREADS = 4;
const int CACHE_SIZE = 512;
const int OBJ_COUNT = 3 * CACHE_SIZE;
DoubleBarrelLRUCache<CloneableObject, object> c = new DoubleBarrelLRUCache<CloneableObject, object>(1024);
CloneableObject[] objs = new CloneableObject[OBJ_COUNT];
for (int i = 0; i < OBJ_COUNT; i++)
{
objs[i] = new CloneableObject(new object());
}
CacheThread[] threads = new CacheThread[NUM_THREADS];
long endTime = (J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond) + 1000L; // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results
for (int i = 0; i < NUM_THREADS; i++)
{
threads[i] = new CacheThread(this, c, objs, endTime);
threads[i].Start();
}
for (int i = 0; i < NUM_THREADS; i++)
{
threads[i].Join();
Assert.False(threads[i].failed);
}
//System.out.println("hits=" + totHit + " misses=" + totMiss);
}
private class CloneableObject : DoubleBarrelLRUCache.CloneableKey
{
private readonly object value;
public CloneableObject(object value)
{
this.value = value;
}
public override bool Equals(object other)
{
// LUCENENET: Additional type check not present in Java code
if (other is CloneableObject cloneableObject)
return this.value.Equals(cloneableObject.value);
else
return false;
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override object Clone()
{
return new CloneableObject(value);
}
}
protected internal class CloneableInteger : DoubleBarrelLRUCache.CloneableKey
{
internal int value;
public CloneableInteger(int value)
{
this.value = value;
}
public override bool Equals(object other)
{
return this.value.Equals(((CloneableInteger)other).value);
}
public override int GetHashCode()
{
return value.GetHashCode();
}
public override object Clone()
{
return new CloneableInteger(value);
}
}
}
}