📄 clock.java
字号:
/* Derby - Class org.apache.derby.impl.services.cache.Clock Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed 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. */package org.apache.derby.impl.services.cache;import org.apache.derby.iapi.services.cache.CacheManager;import org.apache.derby.iapi.services.cache.Cacheable;import org.apache.derby.iapi.services.cache.CacheableFactory;import org.apache.derby.iapi.services.cache.SizedCacheable;import org.apache.derby.iapi.services.context.ContextManager;import org.apache.derby.iapi.services.daemon.DaemonService;import org.apache.derby.iapi.services.daemon.Serviceable;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.services.monitor.Monitor;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.services.cache.ClassSize;import org.apache.derby.iapi.util.Matchable;import org.apache.derby.iapi.util.Operator;import org.apache.derby.iapi.reference.SQLState;import java.util.ArrayList;import java.util.Hashtable;import java.util.Properties;/** A cache manager that uses a Hashtable and a ArrayList. The ArrayList holds CachedItem objects, each with a holder object. The Hashtable is keyed by the identity of the holder object (Cacheable.getIdentity()) and the data portion is a pointer to the CachedItem. CachedItems that have holder objects with no identity do not have entries in the hashtable. <P> CachedItems can in various state. <UL> <LI>isValid - the entry has a valid identity <LI>inCreate - the entry is being created or being faulted in from persistent store <LI>inClean - the entry is being written out to persistent store <LI>isKept - the entry is currently being looked at/updated, do not remove or clean it. </OL> <P>Multithreading considerations:<BR> A clock cache manager must be MT-safe. All member variables are accessed single threaded (synchronized on this) or set once or readonly. Assumptions: holders size() and addElement must be synchronized. <BR> CachedItem is never passed out of the clock cache manager, only the Cacheable object is. The cachedItem is responsible for the setting and clearing of its own member fields (RESOLVE: now they are done in cache manager, need to be moved to the cachedItem). The cache manager will following the following rules while accessing a cacheditem: <UL> <LI>invalid item is never returned from the cache <LI>setValidState and isValid() is only called single threaded through the cache manager. <LI>keep() and isKept() is only called single threaded through the cache manager once the item has been added to the holders array <LI>item that isKept() won't be cleaned or removed or invalidated from the cache. <LI>item that is inClean() or inCreate(), the cache manager will wait on the cachedItem to finish cleaning or creating before it returns the cached item outside of the cache. </UL> <BR> The cacheable must be cleaned thru the cache if it is managed by a cache. On CacheItem, a inClean state is maintain to stablelize the content of the cacheable while it is being cleaned. Only unkept items are cleaned. If an item is found to be inClean, it will wait until it exits the inClean state. If a cached item calls it own clean method without notifying the cache, it has to stablize its content for the duration of the clean. <BR> It is assumed that the cacheable object maintain its own MT-safeness.<BR> @see CachedItem @see Cacheable*/final class Clock extends Hashtable implements CacheManager, Serviceable { /* ** Fields */ public final CacheStat stat; private DaemonService cleaner; // the background worker thread who is going to // do pre-flush for this cache. private final ArrayList holders; private int validItemCount = 0; private long maximumSize; private boolean useByteCount; // regulate the total byte count or the entry count private long currentByteCount = 0; /* currentByteCount should be the sum of entry.getSize() for all entries in the cache. * That is, it should be the sum of getItemSize( item, false) for each item in the holders * vector. */ private static final int ITEM_OVERHEAD = ClassSize.estimateBaseFromCatalog( CachedItem.class) + ClassSize.getRefSize() // one ref per item in the holder ArrayList + ClassSize.estimateHashEntrySize(); private final CacheableFactory holderFactory; private boolean active; // true if active for find/create private String name; // name of the cache, mainly for debugging purposes. private int clockHand; // the sweep of the clock hand private int myClientNumber; // use this number to talk to cleaner service private boolean wokenToClean; // true if the client was woken to clean, false if to shrink private boolean cleanerRunning; private boolean needService; /** Construct a new clock cache manager. <P>MT - not needed for constructor. @param holderClass the cacheable object class @param name the name of the cache @param initParam parameter to call the cacheable object the first time it is being initialized. @param initialSize the initial number of cachable object this cache holds. @param maximumSize the maximum size of the cache. The cache may grow from initialSize to maximumSize if the cache policy notices that there is not enough free buffers availiable. Once the cache hits maximumSize it will not grow. If the cache is full, an exception will be thrown */ Clock(CacheableFactory holderFactory, String name, int initialSize, long maximumSize, boolean useByteCount) { super(initialSize, (float) 0.95); this.maximumSize = maximumSize; this.holderFactory = holderFactory; this.useByteCount = useByteCount; if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { SanityManager.DEBUG(ClockFactory.CacheTrace, "initializing " + name + " cache to size " + initialSize); } } //int delta = initialSize / 2; //if (delta < 5) // delta = 5; holders = new ArrayList(initialSize); this.name = name; active = true; this.stat = new CacheStat(); stat.initialSize = initialSize; stat.maxSize = maximumSize; } /** Find the object or materialize one in the cache. If it has not been created in the persistent store yet, return null. <P>MT - must be MT-safe. The cache is single threaded through finding the item in cache and finding a free item if it is not in cache, thus preventing another thread from creating the same item while is is being faulted in. (RESOLVE - this is really low performance if the cache cleaner cannot keep a steady supply of free items and we have to do an I/O while blocking the cache). If it needs to be faulted in, the inCreate bit is set. The item is kept before it exits the sync block. <BR> If the item is in cache but in the middle of being faulted in or cleaned, it needs to wait until this is done being before returning. <BR> The keep status prevents other threads from removing this item. The inCreate status prevents other threads from looking at or writing out this item while it is being faulted in. (RESOLVE: need to handle the case where the object is marked for removal and being waited on) @param key the key to the object @return a cacheable object that is kept in the cache. @exception StandardException Cloudscape Standard error policy */ public Cacheable find(Object key) throws StandardException { CachedItem item; boolean add; /* ** We will only loop if someone else tried to add the ** same key as we did and they failed. In this case ** we start all over. An example of this would be an ** attempt to cache an object that failed due to a ** transient error (e.g. deadlock), which should not ** prevent another thread from trying to add the ** key to the cache (e.g. it might be the one holding ** the lock that caused the other thread to deadlock). */ while (true) { add = false; synchronized (this) { if (!active) return null; item = (CachedItem) get(key); if (item != null) { item.keepAfterSearch(); stat.findHit++; if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { SanityManager.DEBUG(ClockFactory.CacheTrace, name + ": Found key " + key + " already in cache, item " + item); } } } } // no entry was found, need to add one if (item == null) { // get a free item item = findFreeItem(); stat.findMiss++; if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { SanityManager.DEBUG(ClockFactory.CacheTrace, name + ": Find key " + key + " Not in cache, get free item " + item); } } if (SanityManager.DEBUG) SanityManager.ASSERT(item != null, "found null item"); synchronized (this) { CachedItem inCacheItem = (CachedItem) get(key); if (inCacheItem != null) { // some-one beat us to adding an item into the cache, // just use that one item.unkeepForCreate(); item = inCacheItem; item.keepAfterSearch(); } else { // yes, we really are the ones to add it put(key, item); add = true; if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON("memoryLeakTrace")) { if (size() > ((11 * maximumSize) / 10)) System.out.println("memoryLeakTrace:Cache:" + name + " " + size()); } } } } } if (add) { if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) { SanityManager.DEBUG(ClockFactory.CacheTrace, name + " Added " + key + " to cache, item " + item); } } stat.findFault++; return addEntry(item, key, false, (Object) null); } Cacheable entry = item.use(); if (entry == null) { // item was not added by the other user successfully ... synchronized (this) { item.unkeep(); } // try to hash the key again (see // comment at head of loop) continue; } return entry; } } /** Find an object in the cache. Do not fault in or create the object if is is not found in the cache. <P>MT - must be MT-safe. The cache is single threaded through finding the item in cache. If it needs to wait for it to be faulted in or cleaned it is synchronized/waited on the cached item itself. @param key the key to the object @return a cacheable object that is kept in the cache. */ public Cacheable findCached(Object key) throws StandardException { CachedItem item; synchronized (this) { if (!active) return null; item = (CachedItem) get(key); if (item == null) { stat.findCachedMiss++; return null; } else stat.findCachedHit++; item.keepAfterSearch(); } Cacheable entry = item.use(); if (entry == null) { // item was not added by the other user successfully ... synchronized (this) { item.unkeep(); } } return entry; } /** * Mark a set of entries as having been used. Normally this is done as a side effect * of find() or findCached. Entries that are no longer in the cache are ignored. * * @param key the key of the used entry. */ public void setUsed( Object[] keys) { CachedItem item; for( int i = 0; i < keys.length;) { // Do not hold the synchronization lock for too long. synchronized (this) { if (!active) return; int endIdx = i + 32; if( endIdx > keys.length) endIdx = keys.length; for( ; i < endIdx; i++) { if( keys[i] == null) return; item = (CachedItem) get(keys[i]); if( null != item) item.setUsed( true); } } } } // end of setUsed /** Create a new object with the said key. <P>MT - must be MT-safe. Single thread thru verifying no such item exist in cache and finding a free item, keep the item and set inCreate state. The actual creating of the object is done outside the sync block and is protected by the isKept and inCreate bits. @param key the key to the object @return a cacheable object that is kept in the cache. @exception StandardException Cloudscape Standard error policy */ public Cacheable create(Object key, Object createParameter) throws StandardException { // assume the item is not already in the cache CachedItem item = findFreeItem(); stat.create++; synchronized (this) { if (!active) return null; if (get(key) != null) { item.unkeepForCreate(); throw StandardException.newException(SQLState.OBJECT_EXISTS_IN_CACHE, this.name, key); } put(key, item); if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON("memoryLeakTrace")) { if (size() > ((11 * maximumSize) / 10)) System.out.println("memoryLeakTrace:Cache:" + name + " " + size()); } } } Cacheable entry = addEntry(item, key, true, createParameter); if (SanityManager.DEBUG) { if (entry != null) SanityManager.ASSERT(item.getEntry() == entry); } return entry; } /** The caller is no longer looking at or updating the entry. Since there could be more than one piece of code looking at this entry, release does not mean nobody is looking at or updating the entry, just one less. If the cacheable is marked for remove (someone is waiting to remove the persistent object once nobody is looking at it), then notify the waiter if this is the last one looking at it.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -