--- /dev/null
+package org.apache.lucene.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.
+*/
+
+import org.apache.lucene.util.LuceneTestCase;
+
+public class TestDoubleBarrelLRUCache extends LuceneTestCase {
+
+ private void testCache(DoubleBarrelLRUCache<CloneableInteger,Object> cache, int n) throws Exception {
+ 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) {
+ assertNotNull(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) {
+ assertNotNull(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) {
+ assertNotNull(cache.get(new CloneableInteger(i)));
+ }
+ }
+
+ public void testLRUCache() throws Exception {
+ final int n = 100;
+ testCache(new DoubleBarrelLRUCache<CloneableInteger,Object>(n), n);
+ }
+
+ private class CacheThread extends Thread {
+ private final CloneableObject[] objs;
+ private final DoubleBarrelLRUCache<CloneableObject,Object> c;
+ private final long endTime;
+ volatile boolean failed;
+
+ public CacheThread(DoubleBarrelLRUCache<CloneableObject,Object> c,
+ CloneableObject[] objs, long endTime) {
+ this.c = c;
+ this.objs = objs;
+ this.endTime = endTime;
+ }
+
+ @Override
+ public void run() {
+ try {
+ long count = 0;
+ long miss = 0;
+ long hit = 0;
+ final int limit = objs.length;
+
+ while(true) {
+ final CloneableObject obj = objs[(int) ((count/2) % limit)];
+ Object v = c.get(obj);
+ if (v == null) {
+ c.put(new CloneableObject(obj), obj);
+ miss++;
+ } else {
+ assert obj == v;
+ hit++;
+ }
+ if ((++count % 10000) == 0) {
+ if (System.currentTimeMillis() >= endTime) {
+ break;
+ }
+ }
+ }
+
+ addResults(miss, hit);
+ } catch (Throwable t) {
+ failed = true;
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ long totMiss, totHit;
+ void addResults(long miss, long hit) {
+ totMiss += miss;
+ totHit += hit;
+ }
+
+ public void testThreadCorrectness() throws Exception {
+ final int NUM_THREADS = 4;
+ final int CACHE_SIZE = 512;
+ final 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());
+ }
+
+ final CacheThread[] threads = new CacheThread[NUM_THREADS];
+ final long endTime = System.currentTimeMillis()+1000L;
+ for(int i=0;i<NUM_THREADS;i++) {
+ threads[i] = new CacheThread(c, objs, endTime);
+ threads[i].start();
+ }
+ for(int i=0;i<NUM_THREADS;i++) {
+ threads[i].join();
+ assert !threads[i].failed;
+ }
+ //System.out.println("hits=" + totHit + " misses=" + totMiss);
+ }
+
+ private static class CloneableObject extends DoubleBarrelLRUCache.CloneableKey {
+ private Object value;
+
+ public CloneableObject(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return this.value.equals(((CloneableObject) other).value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public Object clone() {
+ return new CloneableObject(value);
+ }
+ }
+
+ protected static class CloneableInteger extends DoubleBarrelLRUCache.CloneableKey {
+ private Integer value;
+
+ public CloneableInteger(Integer value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return this.value.equals(((CloneableInteger) other).value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public Object clone() {
+ return new CloneableInteger(value);
+ }
+ }
+
+
+}