Merge branch 'xml-fixes' of https://github.com/myeisha/mono into myeisha-xml-fixes
[mono.git] / mcs / class / System.Web / System.Web.Caching / CacheItemLRU.cs
1 //
2 // A simple LRU cache used for tracking the cache items
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnome.org)
6 //
7 // Copyright 2010 Miguel de Icaza
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26 // 
27 using System;
28 using System.Collections.Generic;
29
30 namespace System.Web.Caching
31 {
32         sealed class CacheItemLRU
33         {
34                 public delegate bool SelectItemsQualifier (CacheItem item);
35                 
36                 Dictionary<string, LinkedListNode <CacheItem>> dict;
37                 Dictionary<LinkedListNode<CacheItem>, string> revdict;
38                 LinkedList<CacheItem> list;
39                 Cache owner;
40                 
41                 // High/Low water mark is here to avoid situations when we hit a limit, evict an
42                 // entry, add another one and have to evict again because the limit was hit. When we
43                 // hit the high water limit, we evict until we reach the low water mark to avoid the
44                 // situation.
45                 int highWaterMark, lowWaterMark;
46                 bool needsEviction;
47
48                 public int Count {
49                         get { return dict.Count; }
50                 }
51
52                 public CacheItemLRU (Cache owner, int highWaterMark, int lowWaterMark)
53                 {
54                         list = new LinkedList<CacheItem> ();
55                         dict = new Dictionary<string, LinkedListNode<CacheItem>> (StringComparer.Ordinal);
56                         revdict = new Dictionary<LinkedListNode<CacheItem>, string> ();
57                         
58                         this.highWaterMark = highWaterMark;
59                         this.lowWaterMark = lowWaterMark;
60                         this.owner = owner;
61                 }
62
63                 public bool TryGetValue (string key, out CacheItem value)
64                 {
65                         LinkedListNode <CacheItem> item;
66                         
67                         if (dict.TryGetValue (key, out item)) {
68                                 value = item.Value;
69                                 return true;
70                         }
71                         value = null;
72                         return false;
73                 }
74
75                 // Must ALWAYS be called with the owner's write lock held
76                 public void EvictIfNecessary ()
77                 {
78                         if (!needsEviction)
79                                 return;
80
81                         for (int i = dict.Count; i > lowWaterMark; i--) {
82                                 var key = revdict [list.Last];
83
84                                 owner.Remove (key, CacheItemRemovedReason.Underused, false, true);
85                         }
86                 }
87
88                 // Must ALWAYS be called with the owner's read lock held
89                 public void InvokePrivateCallbacks ()
90                 {
91                         foreach (var de in dict) {
92                                 CacheItem item = de.Value.Value;
93                                 if (item == null || item.Disabled)
94                                         continue;
95                                 
96                                 if (item.OnRemoveCallback != null) {
97                                         try {
98                                                 item.OnRemoveCallback (de.Key, item.Value, CacheItemRemovedReason.Removed);
99                                         } catch {
100                                                 //TODO: anything to be done here?
101                                         }
102                                 }
103                         }
104                 }
105
106                 // Must ALWAYS be called with the owner's write lock held
107                 public List <CacheItem> SelectItems (SelectItemsQualifier qualifier)
108                 {
109                         var ret = new List <CacheItem> ();
110
111                         foreach (LinkedListNode <CacheItem> node in dict.Values) {
112                                 CacheItem item = node.Value;
113                                 
114                                 if (qualifier (item))
115                                         ret.Add (item);
116                         }
117
118                         return ret;
119                 }
120                 
121                 // Must ALWAYS be called with the owner's read lock held
122                 public List <CacheItem> ToList ()
123                 {
124                         var ret = new List <CacheItem> ();
125
126                         if (dict.Count == 0)
127                                 return ret;
128
129                         foreach (LinkedListNode <CacheItem> node in dict.Values)
130                                 ret.Add (node.Value);
131
132                         return ret;
133                 }
134                 
135                 public void Remove (string key)
136                 {
137                         if (key == null)
138                                 return;
139                         
140                         LinkedListNode <CacheItem> node;
141                         if (!dict.TryGetValue (key, out node))
142                                 return;
143
144                         CacheItem item = node.Value;
145                         dict.Remove (key);
146
147                         if (item == null || item.Priority != CacheItemPriority.NotRemovable) {
148                                 revdict.Remove (node);
149                                 list.Remove (node);
150                         }
151                 }
152                 
153                 public CacheItem this [string key] {
154                         get {
155                                 if (key == null)
156                                         return null;
157                                 
158                                 LinkedListNode<CacheItem> node;
159                                 CacheItem item;
160                                 
161                                 if (dict.TryGetValue (key, out node)){
162                                         item = node.Value;
163                                         if (item == null || item.Priority != CacheItemPriority.NotRemovable) {
164                                                 list.Remove (node);
165                                                 list.AddFirst (node);
166                                         }
167                                         
168                                         return item;
169                                 }
170
171                                 return null;
172                         }
173
174                         set {
175                                 LinkedListNode<CacheItem> node;
176         
177                                 if (dict.TryGetValue (key, out node)){
178                                         // If we already have a key, move it to the front
179                                         list.Remove (node);
180                                         if (value == null || value.Priority != CacheItemPriority.NotRemovable)
181                                                 list.AddFirst (node);
182                                         else
183                                                 revdict.Remove (node);
184                                         
185                                         node.Value = value;
186                                         return;
187                                 }
188                                 needsEviction = dict.Count >= highWaterMark;
189                                 
190                                 // Adding new node
191                                 node = new LinkedListNode<CacheItem> (value);
192                                 if (value == null || value.Priority != CacheItemPriority.NotRemovable) {
193                                         list.AddFirst (node);
194                                         revdict [node] = key;
195                                 }
196                                 
197                                 dict [key] = node;
198                         }
199                 }
200         }
201 }