Quadcap Embeddable Database

com/quadcap/sql/file/Cache.java

Go to the documentation of this file.
00001 package com.quadcap.sql.file; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.IOException; 00042 import java.io.PrintWriter; 00043 00044 import java.util.Enumeration; 00045 import java.util.Iterator; 00046 import java.util.Hashtable; 00047 00048 import com.quadcap.util.Debug; 00049 import com.quadcap.util.DList; 00050 import com.quadcap.util.DListItem; 00051 import com.quadcap.util.ListException; 00052 00053 import com.quadcap.util.collections.LongMap; 00054 00055 /** 00056 * This class manages a number of buffers on an underlying store. 00057 * <p> 00058 * 00059 * What about write-through policies? 00060 * 00061 * @author Stan Bailes 00062 */ 00063 public abstract class Cache { 00064 Object store; 00065 Object lock; 00066 int size; 00067 boolean readOnly; 00068 00069 /** 00070 * Map long -> Cacheable 00071 */ 00072 LongMap t; 00073 00074 /** 00075 * LRU list of free cache items 00076 */ 00077 DList lru = new DList(); 00078 00079 /** 00080 * Initialize the cache. 00081 * 00082 * @param s the underlying store 00083 * @param size the size of the cache 00084 */ 00085 public void init(Object store, int size) { 00086 this.store = store; 00087 this.size = size; 00088 if (this.lock == null) this.lock = this; 00089 lru.resize(size); 00090 t = new LongMap(size); 00091 } 00092 00093 /** 00094 * Am I read only? 00095 */ 00096 public boolean isReadOnly() { return readOnly; } 00097 00098 /** 00099 * Set the 'read only' flag 00100 */ 00101 public void setReadOnly(boolean v) { this.readOnly = v; } 00102 00103 /** 00104 * Specify the lock-object to be used to synchronize access to the 00105 * cache. If the lock isn't specified, it defaults to the cache 00106 * object itself. 00107 * 00108 * @param lock the lock object 00109 */ 00110 public void setLock(Object lock) { 00111 this.lock = lock; 00112 } 00113 00114 /** 00115 * Search the cache for the specified key and return the associated 00116 * cache item if one was found. Otherwise, find a free cache item 00117 * and initialize it from the underlying store. 00118 * 00119 * @param key the search key 00120 * @return the cache item associated with the specified key. 00121 */ 00122 public Cacheable getCacheable(long key) throws IOException { 00123 synchronized (lock) { 00124 // ---- check the cache first. 00125 Cacheable c = (Cacheable)t.get(key); 00126 if (c == null) { 00127 // ---- if not in the cache, find a free cache slot and 00128 // ---- fill it with this object. 00129 c = getCacheable(); 00130 c.init(store, key); 00131 c.setReadOnly(readOnly); 00132 t.put(key, c); 00133 } 00134 00135 // ---- move this object to the front of the LRU list. 00136 try { 00137 lru.moveFront(c.getDListItem()); 00138 } catch (ListException e) { 00139 //#ifdef DEBUG 00140 Debug.print(e); 00141 //#endif 00142 } 00143 c.incrRefCount(); 00144 if (lru.size() != size) throw new RuntimeException("cache size!"); 00145 //checkCache(); 00146 return c; 00147 } 00148 } 00149 00150 /** 00151 * Get the specified object from the cache, if available; from the 00152 * underlying store if not. In any case, the object (if found) 00153 * should be in the cache after this call.<p> 00154 * 00155 * If the object isn't in the cache <b>OR</b> the underlying store, 00156 * this method should return null, and the contents of both the cache 00157 * and the underlying store should remain unchanged.<p> 00158 * 00159 * <b>This implementation doesn't change the underlying store, but 00160 * the cache ends up holding a CacheItem with a null data component.</b> 00161 * @param key the search key 00162 * @param return the data associated with the key 00163 */ 00164 public Object get(int key) throws IOException { 00165 Object data = null; 00166 synchronized (lock) { 00167 Cacheable c = getCacheable(key); 00168 try { 00169 data = c.getData(); 00170 } finally { 00171 c.decrRefCount(); 00172 } 00173 } 00174 return data; 00175 } 00176 00177 /** 00178 * Store the object in the cache. The object will be marked <i>dirty</i>, 00179 * and will eventually be written back to the underlying store. 00180 */ 00181 public void put(int key, Object val) throws IOException { 00182 synchronized (lock) { 00183 Cacheable c = getCacheable(key); 00184 try { 00185 c.setData(val); 00186 } finally { 00187 c.decrRefCount(); 00188 } 00189 } 00190 } 00191 00192 /** 00193 * Factory to create an empty cache slot. Only a fixed number of 00194 * cache slots should be created, and they should then be recycled, 00195 * never destroyed. 00196 */ 00197 abstract public Cacheable makeCacheable(); 00198 00199 /** 00200 * Find a free cache slot. If necessary, seize an existing cache 00201 * slot, with preference for the "least recently used" item. 00202 * This function should only be called if the lock on the cache is 00203 * held by this thread. 00204 */ 00205 private final Cacheable getCacheable() throws IOException { 00206 Cacheable c = null; 00207 DListItem d = lru.tail(); 00208 00209 while (d != null) { 00210 c = (Cacheable)d.obj; 00211 if (c != null && c.getRefCount() > 0) { 00212 if (d == lru.head()) { 00213 throw new RuntimeException("no free cache item: cache size = " + lru.size() + "(" + size + ")"); 00214 } 00215 d = d.prev; 00216 } else { 00217 break; // 'c' contains the Cacheable we're looking for 00218 } 00219 } 00220 00221 if (c == null) { 00222 d.obj = c = makeCacheable(); 00223 c.setDListItem(d); 00224 } else { 00225 long key = c.getKey(); 00226 t.remove(key); 00227 if (c.isDirty()) c.flush(); 00228 } 00229 return c; 00230 } 00231 00232 /** 00233 * Flush all modified items back to the underlying store. 00234 */ 00235 public void flush() throws IOException { 00236 synchronized (lock) { 00237 boolean started = false; 00238 for (DListItem d = lru.head(); !started || d != lru.head(); 00239 d = d.next) { 00240 started = true; 00241 Cacheable c = (Cacheable)d.obj; 00242 if (c != null) { 00243 c.flush(); 00244 } 00245 } 00246 } 00247 } 00248 00249 public void revert() { 00250 synchronized (lock) { 00251 lru = new DList(); 00252 init(store, size); 00253 } 00254 } 00255 00256 public void show(PrintWriter os) { 00257 synchronized (lock) { 00258 lru.show(os, "\n"); 00259 } 00260 } 00261 }