KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > util > CacheLRU


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.util;
6
7 import java.sql.SQLException JavaDoc;
8
9 import org.h2.engine.Constants;
10 import org.h2.message.Message;
11
12 /**
13  * Special behavior of the cache: You are not allowed to add the same record
14  * twice.
15  *
16  * @author Thomas
17  */

18 public class CacheLRU implements Cache {
19     
20     public static final String JavaDoc TYPE_NAME = "LRU";
21
22     private int len;
23     private int maxSize;
24     private CacheObject[] values;
25     private int mask;
26     private CacheWriter writer;
27     private int sizeRecords;
28     private int sizeBlocks;
29     private CacheObject head = new CacheHead();
30
31     public CacheLRU(CacheWriter writer, int maxSize) {
32         this.writer = writer;
33         this.len = maxSize / 2;
34         this.mask = len - 1;
35         MathUtils.checkPowerOf2(len);
36         this.maxSize = maxSize;
37         clear();
38     }
39
40     public void put(CacheObject rec) throws SQLException JavaDoc {
41         if(Constants.CHECK) {
42             for(int i=0; i<rec.getBlockCount(); i++) {
43                 CacheObject old = find(rec.getPos() + i);
44                 if(old != null) {
45                     throw Message.getInternalError("try to add a record twice i="+i);
46                 }
47             }
48         }
49         int index = rec.getPos() & mask;
50         rec.chained = values[index];
51         values[index] = rec;
52         sizeRecords++;
53         sizeBlocks += rec.getBlockCount();
54         addToFront(rec);
55         removeOld();
56     }
57     
58     public CacheObject update(int pos, CacheObject rec) throws SQLException JavaDoc {
59         CacheObject old = find(pos);
60         if(old == null) {
61             put(rec);
62         } else {
63             if(Constants.CHECK) {
64                 if(old!=rec) {
65                     throw Message.getInternalError("old != record old="+old+" new="+rec);
66                 }
67             }
68             removeFromLinkedList(rec);
69             addToFront(rec);
70         }
71         return old;
72     }
73     
74     private void removeOld() throws SQLException JavaDoc {
75         if(sizeBlocks < maxSize) {
76             return;
77         }
78         int i=0;
79         ObjectArray changed = new ObjectArray();
80         while (sizeBlocks*4 > maxSize*3 && sizeRecords > Constants.CACHE_MIN_RECORDS) {
81             CacheObject last = head.next;
82             if(i++ >= sizeRecords) {
83                 // can't remove any record, because the log is not written yet
84
// hopefully this does not happen too much, but it could happen theoretically
85
// TODO log this
86
break;
87             }
88             if(Constants.CHECK && last == head) {
89                 throw Message.getInternalError("try to remove head");
90             }
91             // we are not allowed to remove it if the log is not yet written
92
// (because we need to log before writing the data)
93
// also, can't write it if the record is pinned
94
if(!last.canRemove()) {
95                 removeFromLinkedList(last);
96                 addToFront(last);
97                 continue;
98             }
99             remove(last.getPos());
100             if (last.isChanged()) {
101                 changed.add(last);
102             }
103         }
104         if(changed.size() > 0) {
105             CacheObject.sort(changed);
106             for(i=0; i<changed.size(); i++) {
107                 CacheObject rec = (CacheObject) changed.get(i);
108                 writer.writeBack(rec);
109             }
110         }
111     }
112
113     private void addToFront(CacheObject rec) {
114         if(Constants.CHECK && rec == head) {
115             throw Message.getInternalError("try to move head");
116         }
117         rec.next = head;
118         rec.previous = head.previous;
119         rec.previous.next = rec;
120         head.previous = rec;
121     }
122
123     private void removeFromLinkedList(CacheObject rec) {
124         if(Constants.CHECK && rec == head) {
125             throw Message.getInternalError("try to remove head");
126         }
127         rec.previous.next = rec.next;
128         rec.next.previous = rec.previous;
129         // TODO cache: mystery: why is this required? needs more memory if we don't do this
130
rec.next = null;
131         rec.previous = null;
132     }
133
134     public void remove(int pos) {
135         int index = pos & mask;
136         CacheObject rec = values[index];
137         if(rec == null) {
138             return;
139         }
140         if(rec.getPos() == pos) {
141             values[index] = rec.chained;
142         } else {
143             CacheObject last;
144             do {
145                 last = rec;
146                 rec = rec.chained;
147                 if(rec == null) {
148                     return;
149                 }
150             } while(rec.getPos() != pos);
151             last.chained = rec.chained;
152         }
153         sizeRecords--;
154         sizeBlocks -= rec.getBlockCount();
155         removeFromLinkedList(rec);
156         if(Constants.CHECK) {
157             rec.chained = null;
158             if(find(pos) != null) {
159                 throw Message.getInternalError("not removed!");
160             }
161         }
162     }
163     
164     public CacheObject find(int pos) {
165         CacheObject rec = values[pos & mask];
166         while(rec != null && rec.getPos() != pos) {
167             rec = rec.chained;
168         }
169         return rec;
170     }
171
172     public CacheObject get(int pos) {
173         CacheObject rec = find(pos);
174         if(rec != null) {
175             removeFromLinkedList(rec);
176             addToFront(rec);
177         }
178         return rec;
179     }
180     
181 // private void testConsistency() {
182
// int s = size;
183
// HashSet set = new HashSet();
184
// for(int i=0; i<values.length; i++) {
185
// Record rec = values[i];
186
// if(rec == null) {
187
// continue;
188
// }
189
// set.add(rec);
190
// while(rec.chained != null) {
191
// rec = rec.chained;
192
// set.add(rec);
193
// }
194
// }
195
// Record rec = head.next;
196
// while(rec != head) {
197
// set.add(rec);
198
// rec = rec.next;
199
// }
200
// rec = head.previous;
201
// while(rec != head) {
202
// set.add(rec);
203
// rec = rec.previous;
204
// }
205
// if(set.size() != size) {
206
// System.out.println("size="+size+" but el.size="+set.size());
207
// }
208
// }
209

210     public ObjectArray getAllChanged() {
211 // if(Database.CHECK) {
212
// testConsistency();
213
// }
214
// TODO cache: should probably use the LRU list
215
ObjectArray list = new ObjectArray();
216         for (int i = 0; i < len; i++) {
217             CacheObject rec = values[i];
218             while (rec != null) {
219                 if(rec.isChanged()) {
220                     list.add(rec);
221                     if(list.size() >= sizeRecords) {
222                         if(Constants.CHECK) {
223                             if(list.size() > sizeRecords) {
224                                 throw Message.getInternalError("cache chain error");
225                             }
226                         } else {
227                             break;
228                         }
229                     }
230                 }
231                 rec = rec.chained;
232             }
233         }
234         return list;
235     }
236     
237     public void clear() {
238         head.next = head.previous = head;
239         values = new CacheObject[len];
240         sizeRecords = 0;
241         sizeBlocks = 0;
242     }
243     
244     public void setMaxSize(int newSize) throws SQLException JavaDoc {
245         maxSize = newSize < 0 ? 0 : newSize;
246         removeOld();
247     }
248     
249     public String JavaDoc getTypeName() {
250         return TYPE_NAME;
251     }
252
253 }
254
255 // Unmaintained reference code (very old)
256
//import java.util.Iterator;
257
//import java.util.LinkedHashMap;
258
//import java.util.Map;
259
//
260
//public class Cache extends LinkedHashMap {
261
//
262
// final static int MAX_SIZE = 1 << 10;
263
// private CacheWriter writer;
264
//
265
// public Cache(CacheWriter writer) {
266
// super(16, (float) 0.75, true);
267
// this.writer = writer;
268
// }
269
//
270
// protected boolean removeEldestEntry(Map.Entry eldest) {
271
// if(size() <= MAX_SIZE) {
272
// return false;
273
// }
274
// Record entry = (Record) eldest.getValue();
275
// if(entry.getDeleted()) {
276
// return true;
277
// }
278
// if(entry.isChanged()) {
279
// try {
280
////System.out.println("cache write "+entry.getPos());
281
// writer.writeBack(entry);
282
// } catch(SQLException e) {
283
// // TODO cache: printStackTrace not needed if we use our own hashtable
284
// e.printStackTrace();
285
// }
286
// }
287
// return true;
288
// }
289
//
290
// public void put(Record rec) {
291
// put(new Integer(rec.getPos()), rec);
292
// }
293
//
294
// public Record get(int pos) {
295
// return (Record)get(new Integer(pos));
296
// }
297
//
298
// public void remove(int pos) {
299
// remove(new Integer(pos));
300
// }
301
//
302
// public ObjectArray getAllChanged() {
303
// Iterator it = values().iterator();
304
// ObjectArray list = new ObjectArray();
305
// while(it.hasNext()) {
306
// Record rec = (Record)it.next();
307
// if(rec.isChanged()) {
308
// list.add(rec);
309
// }
310
// }
311
// return list;
312
// }
313
//}
314

315
Popular Tags