Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
SegmentCache.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 using System.Text;
22 using System.Threading;
23 
24 using Lucene.Net.Index;
25 
26 namespace Lucene.Net.Util.Cache
27 {
28  /// <summary>
29  /// Root custom cache to allow a factory to retain references to the custom
30  /// caches without having to be aware of the type.
31  /// </summary>
32  public abstract class AbstractSegmentCache
33  {
34  /// <summary>
35  /// Used to warm up the cache.
36  /// </summary>
37  /// <param name="reader">The reader to warm the cache for.</param>
38  /// <param name="key">The inner key.</param>
39  public abstract void Warm(IndexReader reader, string key);
40  }
41 
42  /// <summary>
43  /// Custom cache with two levels of keys, outer key is the IndexReader
44  /// with the inner key being a string, commonly a field name but can be anything.
45  /// Refer to the unit tests for an example implementation.
46  /// <typeparam name="T">The type that is being cached.</typeparam>
47  /// </summary>
48  public abstract class SegmentCache<T> : AbstractSegmentCache
49  {
50  /// <summary>
51  /// The cache - outer key is the reader, inner key is the field name. Value is the item desired.
52  /// </summary>
53  private Dictionary<WeakKey, Dictionary<string, T>> readerCache = new Dictionary<WeakKey, Dictionary<string, T>>();
54 
55  /// <summary>
56  /// Lock to use when accessing the cache.
57  /// </summary>
58  private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
59 
60  /// <summary>
61  /// Value creation.
62  /// </summary>
63  /// <param name="reader">The reader.</param>
64  /// <param name="key">The key to the item under the reader.</param>
65  /// <returns>The value.</returns>
66  protected abstract T CreateValue(IndexReader reader, string key);
67 
68  /// <summary>
69  /// The number of outermost keys in the collection.
70  /// </summary>
71  public int KeyCount
72  {
73  get { return this.readerCache.Count; }
74  }
75 
76  /// <summary>
77  /// Warm the cache - simply calls Get and ignores the return value.
78  /// </summary>
79  /// <param name="reader">The index reader to warm up.</param>
80  /// <param name="key">The key of the item under the reader.</param>
81  public override void Warm(IndexReader reader, string key)
82  {
83  this.Get(reader, key);
84  }
85 
86  /// <summary>
87  /// Get the item from the cache.
88  /// </summary>
89  /// <param name="reader">The IndexReader the cache is from.</param>
90  /// <param name="key">The key of the item under the reader.</param>
91  /// <returns>The item from cache.</returns>
92  public virtual T Get(IndexReader reader, string key)
93  {
94  WeakKey readerRef = new SegmentCache<T>.WeakKey(reader);
95 
96  Dictionary<string, T> innerCache;
97  T retVal = default(T);
98  this.cacheLock.EnterReadLock();
99  try
100  {
101  if (readerCache.TryGetValue(readerRef, out innerCache))
102  {
103  innerCache.TryGetValue(key, out retVal);
104  }
105  }
106  finally
107  {
108  this.cacheLock.ExitReadLock();
109  }
110 
111  if (retVal == null)
112  {
113  retVal = this.CreateValue(reader, key);
114  this.cacheLock.EnterWriteLock();
115  try
116  {
117  if (!readerCache.TryGetValue(readerRef, out innerCache))
118  {
119  innerCache = new Dictionary<string, T>();
120  readerCache.Add(readerRef, innerCache);
121  }
122  if (!innerCache.ContainsKey(key))
123  {
124  innerCache[key] = retVal;
125  }
126  else
127  {
128  // another thread must have put it in while waiting for the write lock
129  // assumption is that the previous thread already flushed the old items
130  return retVal;
131  }
132 
133  // release the old items and yank the gc'd weak references
134  var keys = from wr in this.readerCache.Keys where !wr.IsAlive select wr;
135  List<WeakKey> keysToRemove = keys.ToList();
136  foreach (WeakKey wk in keysToRemove)
137  {
138  this.readerCache.Remove(wk);
139  }
140  }
141  finally
142  {
143  this.cacheLock.ExitWriteLock();
144  }
145  }
146 
147  return retVal;
148  }
149 
150 
151  /// <summary>
152  /// A weak referene wrapper for the hashtable keys. Whenever a key\value pair
153  /// is added to the hashtable, the key is wrapped using a WeakKey. WeakKey saves the
154  /// value of the original object hashcode for fast comparison.
155  /// </summary>
156  internal class WeakKey : WeakReference
157  {
158  /// <summary>
159  /// The hashcode for the target.
160  /// </summary>
161  private int hashCode;
162 
163  /// <summary>
164  /// Create a new WeakKey
165  /// </summary>
166  /// <param name="target">The object to use as the target.</param>
167  internal WeakKey(object target)
168  : base(target)
169  {
170  this.hashCode = target.GetHashCode();
171  }
172 
173  /// <summary>
174  /// The hash code accessor.
175  /// </summary>
176  /// <returns></returns>
177  public override int GetHashCode()
178  {
179  return this.hashCode;
180  }
181 
182  /// <summary>
183  /// Equality between keys.
184  /// </summary>
185  /// <param name="obj">The object to compare to.</param>
186  /// <returns>True if they are equivalent.</returns>
187  public override bool Equals(object obj)
188  {
189  WeakKey other = obj as WeakKey;
190  if (other == null)
191  {
192  return false;
193  }
194 
195  object a = this.Target;
196  object b = other.Target;
197 
198  if (a == null && b == null)
199  {
200  return true;
201  }
202  else if (a == null || b == null)
203  {
204  return false;
205  }
206  else
207  {
208  return a.Equals(b);
209  }
210  }
211  }
212  }
213 }