Source Code Cross Referenced for Clock.java in  » Database-DBMS » db-derby-10.2 » org » apache » derby » impl » services » cache » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database DBMS » db derby 10.2 » org.apache.derby.impl.services.cache 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:           Derby - Class org.apache.derby.impl.services.cache.Clock
0004:
0005:           Licensed to the Apache Software Foundation (ASF) under one or more
0006:           contributor license agreements.  See the NOTICE file distributed with
0007:           this work for additional information regarding copyright ownership.
0008:           The ASF licenses this file to you under the Apache License, Version 2.0
0009:           (the "License"); you may not use this file except in compliance with
0010:           the License.  You may obtain a copy of the License at
0011:
0012:              http://www.apache.org/licenses/LICENSE-2.0
0013:
0014:           Unless required by applicable law or agreed to in writing, software
0015:           distributed under the License is distributed on an "AS IS" BASIS,
0016:           WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017:           See the License for the specific language governing permissions and
0018:           limitations under the License.
0019:
0020:         */
0021:
0022:        package org.apache.derby.impl.services.cache;
0023:
0024:        import org.apache.derby.iapi.services.cache.CacheManager;
0025:        import org.apache.derby.iapi.services.cache.Cacheable;
0026:        import org.apache.derby.iapi.services.cache.CacheableFactory;
0027:        import org.apache.derby.iapi.services.cache.SizedCacheable;
0028:        import org.apache.derby.iapi.services.context.ContextManager;
0029:        import org.apache.derby.iapi.services.daemon.DaemonService;
0030:        import org.apache.derby.iapi.services.daemon.Serviceable;
0031:
0032:        import org.apache.derby.iapi.error.StandardException;
0033:
0034:        import org.apache.derby.iapi.services.monitor.Monitor;
0035:
0036:        import org.apache.derby.iapi.services.sanity.SanityManager;
0037:
0038:        import org.apache.derby.iapi.services.cache.ClassSize;
0039:        import org.apache.derby.iapi.util.Matchable;
0040:        import org.apache.derby.iapi.util.Operator;
0041:        import org.apache.derby.iapi.reference.SQLState;
0042:
0043:        import java.util.ArrayList;
0044:        import java.util.Hashtable;
0045:        import java.util.Properties;
0046:
0047:        /**
0048:         A cache manager that uses a Hashtable and a ArrayList. The ArrayList holds
0049:         CachedItem objects, each with a holder object. The Hashtable is keyed
0050:         by the identity of the holder object (Cacheable.getIdentity()) and
0051:         the data portion is a pointer to the CachedItem. CachedItems that have
0052:         holder objects with no identity do not have entries in the hashtable.
0053:         <P>
0054:         CachedItems can in various state.
0055:         <UL>
0056:         <LI>isValid - the entry has a valid identity
0057:         <LI>inCreate - the entry is being created or being faulted in from persistent store
0058:         <LI>inClean - the entry is being written out to persistent store
0059:         <LI>isKept - the entry is currently being looked at/updated, do not remove or
0060:         clean it.
0061:         </OL>
0062:
0063:         <P>Multithreading considerations:<BR>
0064:         A clock cache manager must be MT-safe.
0065:         All member variables are accessed single threaded (synchronized on this) or
0066:         set once or readonly. Assumptions: holders size() and addElement must be
0067:         synchronized.
0068:         <BR>
0069:         CachedItem is never passed out of the clock cache manager, only the
0070:         Cacheable object is.  The cachedItem is responsible for the setting and
0071:         clearing of its own member fields (RESOLVE: now they are done in cache
0072:         manager, need to be moved to the cachedItem).  The cache manager will
0073:         following the following rules while accessing a cacheditem:
0074:         <UL>
0075:         <LI>invalid item is never returned from the cache
0076:         <LI>setValidState and isValid() is only called single threaded through the cache manager.
0077:         <LI>keep() and isKept() is only called single threaded through the cache
0078:         manager once the item has been added to the holders array
0079:         <LI>item that isKept() won't be cleaned or removed or invalidated from the cache.
0080:         <LI>item that is inClean() or inCreate(), the cache manager
0081:         will wait on the cachedItem to finish cleaning or creating before it
0082:         returns the cached item outside of the cache.
0083:         </UL>
0084:         <BR>
0085:         The cacheable must be cleaned thru the cache if it is managed by a cache.
0086:         On CacheItem, a inClean state is maintain to stablelize the content of the
0087:         cacheable while it is being cleaned.  Only unkept items are cleaned.  If an
0088:         item is found to be inClean, it will wait until it exits the inClean state.
0089:         If a cached item calls it own clean method without notifying the cache, it
0090:         has to stablize its content for the duration of the clean.
0091:         <BR>
0092:         It is assumed that the cacheable object maintain its own MT-safeness.<BR>
0093:
0094:         @see CachedItem
0095:         @see Cacheable
0096:
0097:         */
0098:
0099:        final class Clock extends Hashtable implements  CacheManager,
0100:                Serviceable {
0101:
0102:            /*
0103:             ** Fields
0104:             */
0105:            public final CacheStat stat;
0106:            private DaemonService cleaner; // the background worker thread who is going to
0107:            // do pre-flush for this cache. 
0108:            private final ArrayList holders;
0109:            private int validItemCount = 0;
0110:            private long maximumSize;
0111:            private boolean useByteCount; // regulate the total byte count or the entry count
0112:            private long currentByteCount = 0;
0113:            /* currentByteCount should be the sum of entry.getSize() for all entries in the cache.
0114:             * That is, it should be the sum of getItemSize( item, false) for each item in the holders
0115:             * vector.
0116:             */
0117:
0118:            private static final int ITEM_OVERHEAD = ClassSize
0119:                    .estimateBaseFromCatalog(CachedItem.class)
0120:                    + ClassSize.getRefSize() // one ref per item in the holder ArrayList
0121:                    + ClassSize.estimateHashEntrySize();
0122:
0123:            private final CacheableFactory holderFactory;
0124:
0125:            private boolean active; // true if active for find/create
0126:            private String name; // name of the cache, mainly for debugging purposes.
0127:            private int clockHand; // the sweep of the clock hand
0128:
0129:            private int myClientNumber; // use this number to talk to cleaner service
0130:            private boolean wokenToClean; // true if the client was woken to clean, false if to shrink
0131:            private boolean cleanerRunning;
0132:            private boolean needService;
0133:
0134:            /**
0135:            	Construct a new clock cache manager.
0136:
0137:            	<P>MT - not needed for constructor.
0138:
0139:            	@param holderFactory the cacheable object class
0140:            	@param name the name of the cache
0141:            	@param initialSize the initial number of cachable object this cache
0142:            	holds.
0143:            	@param maximumSize the maximum size of the cache.  The cache may grow
0144:            	from initialSize to maximumSize if the cache policy notices that there
0145:            	is not enough free buffers availiable.  Once the cache hits maximumSize
0146:            	it will not grow.  If the cache is full, an exception will be thrown
0147:
0148:             */
0149:            Clock(CacheableFactory holderFactory, String name, int initialSize,
0150:                    long maximumSize, boolean useByteCount) {
0151:                super (initialSize, (float) 0.95);
0152:                this .maximumSize = maximumSize;
0153:                this .holderFactory = holderFactory;
0154:                this .useByteCount = useByteCount;
0155:
0156:                if (SanityManager.DEBUG) {
0157:                    if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) {
0158:                        SanityManager.DEBUG(ClockFactory.CacheTrace,
0159:                                "initializing " + name + " cache to size "
0160:                                        + initialSize);
0161:                    }
0162:                }
0163:
0164:                //int delta = initialSize / 2;
0165:                //if (delta < 5)
0166:                //	delta = 5;
0167:
0168:                holders = new ArrayList(initialSize);
0169:                this .name = name;
0170:                active = true;
0171:
0172:                this .stat = new CacheStat();
0173:                stat.initialSize = initialSize;
0174:                stat.maxSize = maximumSize;
0175:            }
0176:
0177:            /**
0178:            	Find the object or materialize one in the cache.  If it has not been
0179:            	created in the persistent store yet, return null.
0180:
0181:            	<P>MT - must be MT-safe.  The cache is single threaded through finding
0182:            	the item in cache and finding a free item if it is not in cache, thus
0183:            	preventing another thread from creating the same item while is is being
0184:            	faulted in.  (RESOLVE - this is really low performance if the cache
0185:            	cleaner cannot keep a steady supply of free items and we have to do an
0186:            	I/O while blocking the cache).   If it needs to be faulted in, the
0187:            	inCreate bit is set.  The item is kept before it exits the sync block. 
0188:            	<BR>
0189:            	If the item is in cache but in the middle of being faulted in or
0190:            	cleaned, it needs to wait until this is done being before returning.
0191:            	<BR>
0192:            	The keep status prevents other threads from removing this item.  
0193:            	The inCreate status prevents other threads from looking at or writing
0194:            	out this item while it is being faulted in.
0195:            	(RESOLVE: need to handle the case where the object is marked for
0196:            	removal and being waited on)
0197:
0198:            	@param key the key to the object
0199:            	@return a cacheable object that is kept in the cache.
0200:            	@exception StandardException Cloudscape Standard error policy
0201:             */
0202:            public Cacheable find(Object key) throws StandardException {
0203:                CachedItem item;
0204:                boolean add;
0205:
0206:                /*
0207:                 ** We will only loop if someone else tried to add the
0208:                 ** same key as we did and they failed.  In this case
0209:                 ** we start all over.  An example of this would be an
0210:                 ** attempt to cache an object that failed due to a 
0211:                 ** transient error (e.g. deadlock), which should not
0212:                 ** prevent another thread from trying to add the 
0213:                 ** key to the cache (e.g. it might be the one holding
0214:                 ** the lock that caused the other thread to deadlock).
0215:                 */
0216:                while (true) {
0217:                    add = false;
0218:
0219:                    synchronized (this ) {
0220:
0221:                        if (!active)
0222:                            return null;
0223:
0224:                        item = (CachedItem) get(key);
0225:
0226:                        if (item != null) {
0227:                            item.keepAfterSearch();
0228:
0229:                            stat.findHit++;
0230:
0231:                            if (SanityManager.DEBUG) {
0232:                                if (SanityManager
0233:                                        .DEBUG_ON(ClockFactory.CacheTrace)) {
0234:                                    SanityManager
0235:                                            .DEBUG(
0236:                                                    ClockFactory.CacheTrace,
0237:                                                    name
0238:                                                            + ": Found key "
0239:                                                            + key
0240:                                                            + " already in cache, item "
0241:                                                            + item);
0242:                                }
0243:                            }
0244:                        }
0245:                    }
0246:
0247:                    // no entry was found, need to add one
0248:                    if (item == null) {
0249:
0250:                        // get a free item
0251:                        item = findFreeItem();
0252:
0253:                        stat.findMiss++;
0254:
0255:                        if (SanityManager.DEBUG) {
0256:                            if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) {
0257:                                SanityManager
0258:                                        .DEBUG(
0259:                                                ClockFactory.CacheTrace,
0260:                                                name
0261:                                                        + ": Find key "
0262:                                                        + key
0263:                                                        + " Not in cache, get free item "
0264:                                                        + item);
0265:                            }
0266:                        }
0267:
0268:                        if (SanityManager.DEBUG)
0269:                            SanityManager.ASSERT(item != null,
0270:                                    "found null item");
0271:
0272:                        synchronized (this ) {
0273:                            CachedItem inCacheItem = (CachedItem) get(key);
0274:
0275:                            if (inCacheItem != null) {
0276:                                // some-one beat us to adding an item into the cache,
0277:                                // just use that one
0278:                                item.unkeepForCreate();
0279:
0280:                                item = inCacheItem;
0281:                                item.keepAfterSearch();
0282:                            } else {
0283:                                // yes, we really are the ones to add it
0284:                                put(key, item);
0285:                                add = true;
0286:                                if (SanityManager.DEBUG) {
0287:
0288:                                    if (SanityManager
0289:                                            .DEBUG_ON("memoryLeakTrace")) {
0290:
0291:                                        if (size() > ((11 * maximumSize) / 10))
0292:                                            System.out
0293:                                                    .println("memoryLeakTrace:Cache:"
0294:                                                            + name
0295:                                                            + " "
0296:                                                            + size());
0297:                                    }
0298:                                }
0299:                            }
0300:                        }
0301:                    }
0302:
0303:                    if (add) {
0304:
0305:                        if (SanityManager.DEBUG) {
0306:                            if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) {
0307:                                SanityManager.DEBUG(ClockFactory.CacheTrace,
0308:                                        name + " Added " + key
0309:                                                + " to cache, item " + item);
0310:                            }
0311:                        }
0312:
0313:                        stat.findFault++;
0314:
0315:                        return addEntry(item, key, false, (Object) null);
0316:                    }
0317:
0318:                    Cacheable entry = item.use();
0319:                    if (entry == null) {
0320:                        // item was not added by the other user successfully ...
0321:                        synchronized (this ) {
0322:                            item.unkeep();
0323:                        }
0324:
0325:                        // try to hash the key again (see
0326:                        // comment at head of loop)
0327:                        continue;
0328:                    }
0329:
0330:                    return entry;
0331:                }
0332:            }
0333:
0334:            /**
0335:            	Find an object in the cache.  Do not fault in or create the object if
0336:            	is is not found in the cache.
0337:
0338:            	<P>MT - must be MT-safe.  The cache is single threaded through finding
0339:            	the item in cache.  If it needs to wait for it to be faulted in or
0340:            	cleaned it is synchronized/waited on the cached item itself.
0341:
0342:            	@param key the key to the object
0343:            	@return a cacheable object that is kept in the cache.
0344:             */
0345:
0346:            public Cacheable findCached(Object key) throws StandardException {
0347:
0348:                CachedItem item;
0349:
0350:                synchronized (this ) {
0351:
0352:                    if (!active)
0353:                        return null;
0354:
0355:                    item = (CachedItem) get(key);
0356:
0357:                    if (item == null) {
0358:                        stat.findCachedMiss++;
0359:                        return null;
0360:                    } else
0361:                        stat.findCachedHit++;
0362:
0363:                    item.keepAfterSearch();
0364:                }
0365:
0366:                Cacheable entry = item.use();
0367:                if (entry == null) {
0368:                    // item was not added by the other user successfully ...
0369:                    synchronized (this ) {
0370:                        item.unkeep();
0371:                    }
0372:                }
0373:
0374:                return entry;
0375:            }
0376:
0377:            /**
0378:             * Mark a set of  entries as having been used. Normally this is done as a side effect
0379:             * of find() or findCached. Entries that are no longer in the cache are ignored.
0380:             *
0381:             * @param keys the key of the used entry.
0382:             */
0383:            public void setUsed(Object[] keys) {
0384:                CachedItem item;
0385:
0386:                for (int i = 0; i < keys.length;) {
0387:                    // Do not hold the synchronization lock for too long.
0388:                    synchronized (this ) {
0389:                        if (!active)
0390:                            return;
0391:
0392:                        int endIdx = i + 32;
0393:                        if (endIdx > keys.length)
0394:                            endIdx = keys.length;
0395:                        for (; i < endIdx; i++) {
0396:                            if (keys[i] == null)
0397:                                return;
0398:
0399:                            item = (CachedItem) get(keys[i]);
0400:                            if (null != item)
0401:                                item.setUsed(true);
0402:                        }
0403:                    }
0404:                }
0405:            } // end of setUsed
0406:
0407:            /**
0408:            	Create a new object with the said key.
0409:
0410:            	<P>MT - must be MT-safe.  Single thread thru verifying no such item
0411:            	exist in cache and finding a free item, keep the item and set inCreate
0412:            	state.  The actual creating of the  object is done outside
0413:            	the sync block and is protected by the isKept and inCreate bits.
0414:
0415:            	@param key the key to the object
0416:            	@return a cacheable object that is kept in the cache.  
0417:
0418:            	@exception StandardException Cloudscape Standard error policy
0419:             */
0420:            public Cacheable create(Object key, Object createParameter)
0421:                    throws StandardException {
0422:
0423:                // assume the item is not already in the cache
0424:                CachedItem item = findFreeItem();
0425:
0426:                stat.create++;
0427:
0428:                synchronized (this ) {
0429:
0430:                    if (!active)
0431:                        return null;
0432:
0433:                    if (get(key) != null) {
0434:
0435:                        item.unkeepForCreate();
0436:
0437:                        throw StandardException
0438:                                .newException(SQLState.OBJECT_EXISTS_IN_CACHE,
0439:                                        this .name, key);
0440:                    }
0441:
0442:                    put(key, item);
0443:
0444:                    if (SanityManager.DEBUG) {
0445:
0446:                        if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
0447:
0448:                            if (size() > ((11 * maximumSize) / 10))
0449:                                System.out.println("memoryLeakTrace:Cache:"
0450:                                        + name + " " + size());
0451:                        }
0452:                    }
0453:                }
0454:
0455:                Cacheable entry = addEntry(item, key, true, createParameter);
0456:
0457:                if (SanityManager.DEBUG) {
0458:                    if (entry != null)
0459:                        SanityManager.ASSERT(item.getEntry() == entry);
0460:                }
0461:
0462:                return entry;
0463:            }
0464:
0465:            /**
0466:            	The caller is no longer looking at or updating the entry.  Since there
0467:            	could be more than one piece of code looking at this entry, release
0468:            	does not mean nobody is looking at or updating the entry, just one
0469:            	less.  If the cacheable is marked for remove (someone is waiting to
0470:            	remove the persistent object once nobody is looking at it), then notify
0471:            	the waiter if this is the last one looking at it.
0472:            	<BR>
0473:            	Unless there is a good reason to do otherwise, release should be used
0474:            	to release a cachable and not directly call cachedItem unkeep, since
0475:            	unkeep does not handle the case of remove.
0476:
0477:
0478:            	<P>MT - must be MT-safe.  Getting and deleteing item from the hashtable
0479:            	is in the same synchronized block.  If the cacheable object is waiting
0480:            	to be removed, that is synchronized thru the cachedItem itself
0481:            	(RESOLVE: need to move this sync block to cachedItem instead)
0482:
0483:            	@param entry the cached entry
0484:
0485:             */
0486:            public void release(Cacheable entry) {
0487:                boolean removeItem;
0488:                CachedItem item;
0489:                long toShrink = 0;
0490:
0491:                synchronized (this ) {
0492:
0493:                    item = (CachedItem) get(entry.getIdentity());
0494:
0495:                    if (SanityManager.DEBUG) {
0496:                        SanityManager.ASSERT(item != null, "item null");
0497:                        SanityManager.ASSERT(item.getEntry() == entry,
0498:                                "entry not equals keyed entry");
0499:                        SanityManager.ASSERT(item.isKept(),
0500:                                "item is not kept in release(Cachable)");
0501:                    }
0502:
0503:                    removeItem = item.unkeep();
0504:
0505:                    if (removeItem) {
0506:
0507:                        remove(entry.getIdentity());
0508:
0509:                        // we keep the item here to stop another thread trying to evict it
0510:                        // while we are destroying it.
0511:                        item.keepForClean();
0512:                    }
0513:
0514:                    if (cleaner == null) {
0515:                        // try to shrink the cache on a release
0516:                        toShrink = shrinkSize(getCurrentSize());
0517:                    }
0518:                }
0519:
0520:                if (removeItem) {
0521:
0522:                    item.notifyRemover();
0523:                }
0524:
0525:                if (toShrink > 0)
0526:                    performWork(true /* shrink only */);
0527:            }
0528:
0529:            protected void release(CachedItem item) {
0530:
0531:                boolean removeItem;
0532:
0533:                synchronized (this ) {
0534:
0535:                    if (SanityManager.DEBUG) {
0536:                        SanityManager.ASSERT(item.isKept(),
0537:                                "item is not kept in released(CachedItem)");
0538:                    }
0539:
0540:                    removeItem = item.unkeep();
0541:
0542:                    if (removeItem) {
0543:
0544:                        remove(item.getEntry().getIdentity());
0545:
0546:                        // we keep the item here to stop another thread trying to evict it
0547:                        // while we are destroying it.
0548:                        item.keepForClean();
0549:                    }
0550:                }
0551:
0552:                if (removeItem) {
0553:
0554:                    item.notifyRemover();
0555:                }
0556:            }
0557:
0558:            /**
0559:            	Remove an object from the cache. The item will be placed into the NoIdentity
0560:            	state through clean() (if required) and clearIdentity(). The removal of the
0561:            	object will be delayed until it is not kept by anyone.
0562:
0563:            	After this call the caller must throw away the reference to item.
0564:
0565:            	<P>MT - must be MT-safe.  Single thread thru finding and setting the
0566:            	remove state of the item, the actual removal of the cacheable is
0567:            	synchronized on the cachedItem itself.
0568:
0569:            	@exception StandardException Standard Cloudscape error policy.
0570:             */
0571:            public void remove(Cacheable entry) throws StandardException {
0572:
0573:                boolean removeNow;
0574:                CachedItem item;
0575:                long origItemSize = 0;
0576:
0577:                stat.remove++;
0578:
0579:                synchronized (this ) {
0580:
0581:                    item = (CachedItem) get(entry.getIdentity());
0582:
0583:                    if (SanityManager.DEBUG) {
0584:                        SanityManager.ASSERT(item != null);
0585:                        SanityManager.ASSERT(item.getEntry() == entry);
0586:                        SanityManager.ASSERT(item.isKept());
0587:                    }
0588:                    if (useByteCount)
0589:                        origItemSize = getItemSize(item);
0590:
0591:                    item.setRemoveState();
0592:                    removeNow = item.unkeep();
0593:
0594:                    if (removeNow) {
0595:                        remove(entry.getIdentity());
0596:                        item.keepForClean();
0597:                    }
0598:                }
0599:
0600:                try {
0601:                    // if removeNow is false then this thread may sleep
0602:                    item.remove(removeNow);
0603:
0604:                } finally {
0605:
0606:                    synchronized (this ) {
0607:                        // in the case where this thread didn't call keepForClean() the thread
0608:                        // that woke us would have called keepForClean.
0609:                        item.unkeep();
0610:                        item.setValidState(false);
0611:                        validItemCount--;
0612:                        item.getEntry().clearIdentity();
0613:                        if (useByteCount)
0614:                            currentByteCount += getItemSize(item)
0615:                                    - origItemSize;
0616:                    }
0617:                }
0618:
0619:            }
0620:
0621:            /**
0622:            	Clean all objects in the cache.
0623:             */
0624:            public void cleanAll() throws StandardException {
0625:                stat.cleanAll++;
0626:                cleanCache((Matchable) null);
0627:            }
0628:
0629:            /**
0630:            	Clean all objects that match a partial key.
0631:             */
0632:            public void clean(Matchable partialKey) throws StandardException {
0633:
0634:                cleanCache(partialKey);
0635:            }
0636:
0637:            /**
0638:            	Age as many objects as possible out of the cache.
0639:
0640:            	<BR>MT - thread safe
0641:
0642:            	@see CacheManager#ageOut
0643:             */
0644:            public void ageOut() {
0645:
0646:                stat.ageOut++;
0647:                synchronized (this ) {
0648:
0649:                    int size = holders.size();
0650:                    long toShrink = shrinkSize(getCurrentSize());
0651:                    boolean shrunk = false;
0652:
0653:                    for (int position = 0; position < size; position++) {
0654:                        CachedItem item = (CachedItem) holders.get(position);
0655:
0656:                        if (item.isKept())
0657:                            continue;
0658:                        if (!item.isValid())
0659:                            continue;
0660:
0661:                        if (item.getEntry().isDirty()) {
0662:                            continue;
0663:                        }
0664:
0665:                        long itemSize = removeIdentity(item);
0666:
0667:                        if (toShrink > 0) {
0668:
0669:                            if (SanityManager.DEBUG) {
0670:                                if (SanityManager
0671:                                        .DEBUG_ON(ClockFactory.CacheTrace)) {
0672:                                    SanityManager.DEBUG(
0673:                                            ClockFactory.CacheTrace, name
0674:                                                    + " shrinking item " + item
0675:                                                    + " at position "
0676:                                                    + position);
0677:                                }
0678:                            }
0679:
0680:                            toShrink -= itemSize;
0681:                            shrunk = true;
0682:                        }
0683:
0684:                    } // end of for loop
0685:
0686:                    if (shrunk)
0687:                        trimToSize();
0688:
0689:                } // out of sync block
0690:            } // end of ageOut
0691:
0692:            /**
0693:            	MT - synchronization provided by caller
0694:
0695:            	@exception StandardException Standard Cloudscape error policy.
0696:             */
0697:            public void shutdown() throws StandardException {
0698:
0699:                if (cleaner != null) {
0700:                    cleaner.unsubscribe(myClientNumber);
0701:                    cleaner = null;
0702:                }
0703:
0704:                synchronized (this ) {
0705:                    active = false;
0706:                }
0707:
0708:                ageOut();
0709:                cleanAll();
0710:                ageOut();
0711:            }
0712:
0713:            /**
0714:            	MT - synchronization provided by caller
0715:
0716:            	can use this Daemomn service if needed
0717:             */
0718:            public void useDaemonService(DaemonService daemon) {
0719:                // if we were using another cleaner, unsubscribe first
0720:                if (cleaner != null)
0721:                    cleaner.unsubscribe(myClientNumber);
0722:
0723:                cleaner = daemon;
0724:                myClientNumber = cleaner
0725:                        .subscribe(this , true /* onDemandOnly */);
0726:            }
0727:
0728:            /**
0729:            	Discard all objects that match the partial key.
0730:
0731:            	<BR>MT - thread safe
0732:             */
0733:            public boolean discard(Matchable partialKey) {
0734:
0735:                // we miss something because it was kept
0736:                boolean noMisses = true;
0737:
0738:                synchronized (this ) {
0739:
0740:                    int size = holders.size();
0741:                    long toShrink = shrinkSize(getCurrentSize());
0742:                    boolean shrunk = false;
0743:
0744:                    for (int position = 0; position < size; position++) {
0745:                        CachedItem item = (CachedItem) holders.get(position);
0746:
0747:                        if (!item.isValid())
0748:                            continue;
0749:
0750:                        Object key = item.getEntry().getIdentity();
0751:
0752:                        if (partialKey != null && !partialKey.match(key))
0753:                            continue;
0754:
0755:                        if (item.isKept()) {
0756:                            noMisses = false;
0757:                            continue;
0758:                        }
0759:
0760:                        long itemSize = removeIdentity(item);
0761:
0762:                        if (toShrink > 0) {
0763:
0764:                            if (SanityManager.DEBUG) {
0765:                                if (SanityManager
0766:                                        .DEBUG_ON(ClockFactory.CacheTrace)) {
0767:                                    SanityManager.DEBUG(
0768:                                            ClockFactory.CacheTrace, name
0769:                                                    + " shrinking item " + item
0770:                                                    + " at position "
0771:                                                    + position);
0772:                                }
0773:                            }
0774:
0775:                            // and we shrunk one item
0776:                            toShrink -= itemSize;
0777:                            shrunk = true;
0778:                        }
0779:                    }
0780:
0781:                    if (shrunk)
0782:                        trimToSize();
0783:                }
0784:
0785:                return noMisses;
0786:            }
0787:
0788:            /**
0789:            	Add a new CachedItem and a holder object to the cache. The holder object
0790:            	is returned kept.
0791:
0792:            	<P>MT - need to be MT-safe.  The insertion of the key into the hash
0793:            	table is synchronized on this.
0794:
0795:             */
0796:            private Cacheable addEntry(CachedItem item, Object key,
0797:                    boolean forCreate, Object createParameter)
0798:                    throws StandardException {
0799:
0800:                Cacheable entry = null;
0801:                long origEntrySize = 0;
0802:                if (useByteCount)
0803:                    origEntrySize = getItemSize(item);
0804:
0805:                try {
0806:                    if (SanityManager.DEBUG) {
0807:                        if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace)) {
0808:                            SanityManager.DEBUG(ClockFactory.CacheTrace, name
0809:                                    + " item " + item + " take on identity "
0810:                                    + key);
0811:                        }
0812:                    }
0813:
0814:                    // tell the object it needs to create itself
0815:                    entry = item.takeOnIdentity(this , holderFactory, key,
0816:                            forCreate, createParameter);
0817:                } finally {
0818:                    boolean notifyWaiters;
0819:                    synchronized (this ) {
0820:
0821:                        Object removed = remove(key);
0822:                        if (SanityManager.DEBUG) {
0823:                            SanityManager.ASSERT(removed == item);
0824:                        }
0825:
0826:                        if (entry != null) {
0827:                            // put the actual key into the hash table, not the one that was passed in
0828:                            // for the find or create. This is because the caller may re-use the key
0829:                            // for another cache operation, which would corrupt our hashtable
0830:                            put(entry.getIdentity(), item);
0831:                            if (useByteCount)
0832:                                currentByteCount += ((SizedCacheable) entry)
0833:                                        .getSize()
0834:                                        - origEntrySize;
0835:                            item.setValidState(true);
0836:                            validItemCount++;
0837:                            notifyWaiters = true;
0838:                        } else {
0839:                            item.unkeep();
0840:                            notifyWaiters = item.isKept();
0841:                        }
0842:                    }
0843:
0844:                    // whatever the outcome, we have to notify waiters ...
0845:                    if (notifyWaiters)
0846:                        item.settingIdentityComplete();
0847:                }
0848:
0849:                return entry;
0850:            }
0851:
0852:            protected CachedItem findFreeItem() throws StandardException {
0853:
0854:                // Need to avoid thrashing the cache when we start out
0855:                // so if the cache is smaller than its maximum size
0856:                // then that's a good indication we should grow.
0857:
0858:                long currentSize = getCurrentSize();
0859:
0860:                if (currentSize >= maximumSize) {
0861:                    // look at 20%
0862:                    CachedItem item = rotateClock(0.2f);
0863:                    if (item != null)
0864:                        return item;
0865:                }
0866:
0867:                // However, if the cache contains a large number of invalid
0868:                // items then we should see if we can avoid growing.
0869:                // This avoids simple use of Cloudscape looking like
0870:                // a memory leak, as the page cache fills the holders array
0871:                // with page objects including the 4k (or 32k) pages.
0872:                // size() is the number of valid entries in the hash table
0873:
0874:                // no need to sync on getting the sizes since if they are
0875:                // wrong we will discover it in the loop.
0876:                if (validItemCount < holders.size()) {
0877:
0878:                    synchronized (this ) {
0879:
0880:                        // 1) find out how many invalid items there are in the
0881:                        //    cache
0882:                        // 2) search for a free invalid item
0883:                        // 3) stop searching when there are no more invalid
0884:                        //    items to find
0885:
0886:                        int invalidItems = holders.size() - validItemCount;
0887:
0888:                        // Invalid items might occur in the cache when
0889:                        //   a) a new item is created in growCache(), but it
0890:                        //      is not in use yet, or
0891:                        //   b) an item is deleted (usually when a table is
0892:                        //      dropped)
0893:
0894:                        // It is critical to break out of the loop as soon as
0895:                        // possible since we are blocking others trying to
0896:                        // access the page cache. New items are added to the
0897:                        // end of the page cache, so the search for invalid
0898:                        // items should start from the end.
0899:
0900:                        for (int i = holders.size() - 1; (invalidItems > 0)
0901:                                && (i >= 0); i--) {
0902:                            CachedItem item = (CachedItem) holders.get(i);
0903:
0904:                            if (item.isKept()) {
0905:                                if (!item.isValid())
0906:                                    invalidItems--;
0907:                                continue;
0908:                            }
0909:
0910:                            // found a free item, just use it
0911:                            if (!item.isValid()) {
0912:                                item.keepForCreate();
0913:                                return item;
0914:                            }
0915:                        }
0916:                    }
0917:                }
0918:
0919:                return growCache();
0920:            }
0921:
0922:            /**
0923:            	Go through the list of holder objects and find a free one.
0924:            	<P>MT - must be MT-safe.  The moving of the clockHand and finding of an
0925:            	eviction candidate is synchronized.  The cleaning of the cachable is
0926:            	handled by the cacheable itself.
0927:             */
0928:            protected CachedItem rotateClock(float percentOfClock)
0929:                    throws StandardException {
0930:                // statistics -- only used in debug
0931:                int evictions = 0;
0932:                int cleaned = 0;
0933:                int resetUsed = 0;
0934:                int iskept = 0;
0935:
0936:                // When we are managing the entry count (useByteCount == false) this method just
0937:                // has to find or manufacture an available item (a cache slot). When we are managing
0938:                // the total byte count then this method must find both available space and an
0939:                // available item.
0940:                CachedItem availableItem = null;
0941:
0942:                boolean kickCleaner = false;
0943:
0944:                try {
0945:
0946:                    // this can be approximate
0947:                    int itemCount = holders.size();
0948:                    int itemsToCheck;
0949:                    if (itemCount < 20)
0950:                        itemsToCheck = 2 * itemCount;
0951:                    else
0952:                        itemsToCheck = (int) (((float) itemCount) * percentOfClock);
0953:
0954:                    // if we can grow then shrinking is OK too, if we can't grow
0955:                    // then shrinking the cache won't help us find an item.
0956:                    long toShrink = shrinkSize(getCurrentSize());
0957:
0958:                    restartClock: for (; itemsToCheck > 0;) {
0959:
0960:                        CachedItem item = null;
0961:
0962:                        synchronized (this ) {
0963:
0964:                            if (SanityManager.DEBUG) {
0965:                                if (SanityManager
0966:                                        .DEBUG_ON(ClockFactory.CacheTrace)) {
0967:                                    SanityManager.DEBUG(
0968:                                            ClockFactory.CacheTrace, name
0969:                                                    + " rotateClock starting "
0970:                                                    + clockHand
0971:                                                    + " itemsToCheck "
0972:                                                    + itemsToCheck);
0973:                                }
0974:                            }
0975:
0976:                            // size of holders cannot change while in the synchronized block.
0977:                            int size = holders.size();
0978:                            for (; itemsToCheck > 0; item = null, itemsToCheck--, incrClockHand()) {
0979:                                //
0980:                                // This uses a very simple clock algorithm.
0981:                                //
0982:                                // The cache consist of a circular list of cachedItems.  Each cached item
0983:                                // has a 'recentlyUsed' bit which is set every time that item is kept.
0984:                                // Each clock cache manager keeps a global variable clockHand which
0985:                                // refers to the item that is most recently replaced.
0986:                                //
0987:                                // to find a free item, the clock Hand moves to the next cached Item.
0988:                                // If it is kept, or in the middle of being created, the clock hand
0989:                                // moves on.  
0990:                                // If it is recentlyUsed, clear the recently used bit and moves on. 
0991:                                // If it is not recentlyUsed, clean the item and use
0992:                                //
0993:                                // If all the cached item is kept, then create a new entry.
0994:                                // So it is possible, although very unlikely,  that, in time, the cache
0995:                                // will grow beyond the maximum size.
0996:
0997:                                if (clockHand >= size) {
0998:                                    if (size == 0)
0999:                                        break;
1000:                                    clockHand = 0;
1001:                                }
1002:
1003:                                item = (CachedItem) holders.get(clockHand);
1004:
1005:                                if (item.isKept()) {
1006:                                    if (SanityManager.DEBUG) // stats only in debug mode
1007:                                        iskept++;
1008:                                    continue;
1009:                                }
1010:
1011:                                if (!item.isValid()) // found a free item, just use it
1012:                                {
1013:                                    if (null != availableItem)
1014:                                        // We have found an available item, now we are looking for bytes
1015:                                        continue;
1016:                                    if (SanityManager.DEBUG) {
1017:                                        if (SanityManager
1018:                                                .DEBUG_ON(ClockFactory.CacheTrace)) {
1019:                                            SanityManager
1020:                                                    .DEBUG(
1021:                                                            ClockFactory.CacheTrace,
1022:                                                            name
1023:                                                                    + " found free item at "
1024:                                                                    + clockHand
1025:                                                                    + " item "
1026:                                                                    + item);
1027:                                        }
1028:                                    }
1029:
1030:                                    item.keepForCreate();
1031:                                    if (useByteCount
1032:                                            && getCurrentSize() > maximumSize) {
1033:                                        availableItem = item;
1034:                                        // now look for bytes.
1035:                                        continue;
1036:                                    }
1037:                                    // since we are using this item, move the clock past it.
1038:                                    incrClockHand();
1039:
1040:                                    return item;
1041:                                }
1042:
1043:                                if (item.recentlyUsed()) {
1044:
1045:                                    if (SanityManager.DEBUG) // stats only in debug mode
1046:                                        resetUsed++;
1047:                                    item.setUsed(false);
1048:                                    continue;
1049:                                }
1050:
1051:                                if (toShrink > 0) {
1052:                                    if (!cleanerRunning) {
1053:
1054:                                        // try an get the cleaner to shrink the cache
1055:                                        kickCleaner = true;
1056:                                        cleanerRunning = true;
1057:                                        needService = true;
1058:                                    }
1059:                                }
1060:
1061:                                // we are seeing valid, not recently used buffers. Evict this.
1062:                                if (SanityManager.DEBUG) {
1063:                                    evictions++;
1064:
1065:                                    if (SanityManager
1066:                                            .DEBUG_ON(ClockFactory.CacheTrace)) {
1067:                                        SanityManager.DEBUG(
1068:                                                ClockFactory.CacheTrace, name
1069:                                                        + " evicting item at "
1070:                                                        + clockHand + " item "
1071:                                                        + item);
1072:                                    }
1073:                                }
1074:
1075:                                if (!item.getEntry().isDirty()) {
1076:
1077:                                    if (SanityManager.DEBUG) {
1078:                                        if (SanityManager
1079:                                                .DEBUG_ON(ClockFactory.CacheTrace)) {
1080:                                            SanityManager.DEBUG(
1081:                                                    ClockFactory.CacheTrace,
1082:                                                    name + " Evicting Item "
1083:                                                            + item
1084:                                                            + ", not dirty");
1085:                                        }
1086:                                    }
1087:
1088:                                    // a valid, unkept, clean item, clear its identity
1089:                                    // and use it.
1090:                                    long itemSize = removeIdentity(item);
1091:
1092:                                    if (useByteCount) {
1093:                                        toShrink -= itemSize;
1094:                                        if (getCurrentSize() > maximumSize
1095:                                                && 0 < toShrink) {
1096:                                            if (null == availableItem) {
1097:                                                item.keepForCreate();
1098:                                                availableItem = item;
1099:                                            }
1100:                                            continue;
1101:                                        }
1102:                                    }
1103:                                    // since we are using it move the clock past it
1104:                                    incrClockHand();
1105:
1106:                                    if (null != availableItem)
1107:                                        return availableItem;
1108:
1109:                                    // item is kept but not valid when returned
1110:                                    item.keepForCreate();
1111:                                    return item;
1112:                                }
1113:                                // item is valid, unkept, and dirty. clean it.
1114:                                if ((cleaner != null) && !cleanerRunning) {
1115:                                    kickCleaner = true;
1116:                                    wokenToClean = true;
1117:                                    cleanerRunning = true; // at least it soon will be
1118:                                }
1119:                                item.keepForClean();
1120:
1121:                                // leave the clock hand where it is so that we will pick it
1122:                                // up if no-one else uses the cache. Other hunters will
1123:                                // skip over it as it is kept, and thus move the clock
1124:                                // hand past it.
1125:                                break;
1126:                            }
1127:                            if (item == null) {
1128:                                return availableItem;
1129:                            }
1130:
1131:                        } // out of synchronized block
1132:
1133:                        // clean the entry outside of a sync block				    
1134:                        try {
1135:                            if (SanityManager.DEBUG) {
1136:                                if (SanityManager
1137:                                        .DEBUG_ON(ClockFactory.CacheTrace)) {
1138:                                    SanityManager.DEBUG(
1139:                                            ClockFactory.CacheTrace, name
1140:                                                    + " cleaning item " + item);
1141:                                }
1142:                            }
1143:
1144:                            item.clean(false);
1145:
1146:                            if (SanityManager.DEBUG) // stats only in debug mode
1147:                            {
1148:                                cleaned++;
1149:                            }
1150:                        } finally {
1151:                            release(item);
1152:                            item = null;
1153:                        }
1154:
1155:                        // at this point the item we cleaned could be in any state
1156:                        // so we can't just re-use it. Continue searching
1157:                        continue restartClock;
1158:                    }
1159:                    return availableItem;
1160:                } finally {
1161:
1162:                    if (SanityManager.DEBUG) {
1163:                        // report statistics
1164:                        if (SanityManager.DEBUG_ON(ClockFactory.CacheTrace))
1165:                            SanityManager.DEBUG(ClockFactory.CacheTrace, name
1166:                                    + " evictions " + evictions + ", cleaned "
1167:                                    + cleaned + ", resetUsed " + resetUsed
1168:                                    + ", isKept " + iskept + ", size "
1169:                                    + holders.size());
1170:                    }
1171:
1172:                    if (kickCleaner && (cleaner != null)) {
1173:                        if (SanityManager.DEBUG) {
1174:                            if (SanityManager
1175:                                    .DEBUG_ON(DaemonService.DaemonTrace)) {
1176:                                SanityManager.DEBUG(DaemonService.DaemonTrace,
1177:                                        name + " client # " + myClientNumber
1178:                                                + " calling cleaner ");
1179:                            }
1180:                        }
1181:
1182:                        cleaner.serviceNow(myClientNumber);
1183:
1184:                        if (SanityManager.DEBUG) {
1185:                            if (SanityManager
1186:                                    .DEBUG_ON(DaemonService.DaemonTrace)) {
1187:                                SanityManager.DEBUG(DaemonService.DaemonTrace,
1188:                                        name + Thread.currentThread().getName()
1189:                                                + " cleaner called");
1190:                            }
1191:                        }
1192:                    }
1193:                }
1194:            } // end of rotateClock
1195:
1196:            /**
1197:            	Synchronously increment clock hand position
1198:             */
1199:            private int incrClockHand() {
1200:                if (++clockHand >= holders.size())
1201:                    clockHand = 0;
1202:                return clockHand;
1203:            }
1204:
1205:            /*
1206:             * Serviceable methods
1207:             */
1208:
1209:            public int performWork(ContextManager contextMgr /* ignored */) {
1210:
1211:                int ret = performWork(false);
1212:                synchronized (this ) {
1213:                    cleanerRunning = false;
1214:                }
1215:                return ret;
1216:            }
1217:
1218:            /**
1219:            	<P>MT - read only. 
1220:             */
1221:            public boolean serviceASAP() {
1222:                return needService;
1223:            }
1224:
1225:            // @return true, if this work needs to be done on a user thread immediately
1226:            public boolean serviceImmediately() {
1227:                return false;
1228:            }
1229:
1230:            public synchronized int getNumberInUse() {
1231:
1232:                int size = holders.size();
1233:                int inUse = 0;
1234:
1235:                for (int position = 0; position < size; position++) {
1236:
1237:                    CachedItem item = (CachedItem) holders.get(position);
1238:
1239:                    if (item.isValid()) {
1240:                        inUse++;
1241:                    }
1242:
1243:                }
1244:                return inUse;
1245:            }
1246:
1247:            /*
1248:             public int getNumberKept() {
1249:
1250:             synchronized (this) {
1251:
1252:             int size = holders.size();
1253:             int inUse = 0;
1254:
1255:             for (int position = 0; position < size; position++) {
1256:
1257:             CachedItem item = (CachedItem) holders.get(position);
1258:
1259:             if (item.isValid() && item.isKept()) {
1260:             inUse++;
1261:             }
1262:
1263:             }
1264:             return inUse;
1265:             }
1266:             }
1267:             */
1268:
1269:            /**
1270:            	Grow the cache and return a unused, kept item.
1271:
1272:            	@exception StandardException Thrown if the cache cannot be grown.
1273:             */
1274:
1275:            private CachedItem growCache() {
1276:
1277:                CachedItem item = new CachedItem();
1278:                item.keepForCreate();
1279:
1280:                // if we run out of memory below here we don't
1281:                // know what state the holders could be in
1282:                // so don't trap it
1283:                synchronized (this ) {
1284:                    holders.add(item);
1285:                    // Do not adjust currentByteCount until we put the entry into the CachedItem.
1286:                }
1287:
1288:                return item;
1289:            }
1290:
1291:            /**
1292:            	Clear an item's identity. Item must be 
1293:            	unkept and valid. This is called for
1294:            	dirty items from the discard code.
1295:
1296:            	Caller must hold the cache synchronization.
1297:
1298:                @return the amount by which this shrinks the cache.
1299:             */
1300:            protected long removeIdentity(CachedItem item) {
1301:
1302:                long shrink = 1;
1303:
1304:                if (SanityManager.DEBUG) {
1305:                    SanityManager.ASSERT(!item.isKept(), "item is kept");
1306:                    SanityManager.ASSERT(item.isValid(), "item is not valid");
1307:
1308:                }
1309:
1310:                if (useByteCount)
1311:                    shrink = ((SizedCacheable) item.getEntry()).getSize();
1312:                remove(item.getEntry().getIdentity());
1313:                item.setValidState(false);
1314:                validItemCount--;
1315:                item.getEntry().clearIdentity();
1316:                if (useByteCount) {
1317:                    shrink -= ((SizedCacheable) item.getEntry()).getSize();
1318:                    currentByteCount -= shrink;
1319:                }
1320:                return shrink;
1321:            }
1322:
1323:            /**
1324:            	Write out all dirty buffers.
1325:
1326:            	<P>MT - must be MT safe.
1327:            	Single thread on the part that finds the next dirty buffer to write
1328:            	out, the synchronization of cleaning of the individual cachable is
1329:            	provided by the cacheable itself.
1330:             */
1331:            protected void cleanCache(Matchable partialKey)
1332:                    throws StandardException {
1333:
1334:                int position;
1335:
1336:                synchronized (this ) {
1337:                    // this is at many dirty buffers as the cleaner is ever going to
1338:                    // see 
1339:                    position = holders.size() - 1;
1340:                }
1341:
1342:                outerscan: for (;;) {
1343:
1344:                    CachedItem item = null;
1345:
1346:                    synchronized (this ) {
1347:
1348:                        // the cache may have shrunk by quite a bit since we last came
1349:                        // in here
1350:                        int size = holders.size();
1351:                        if (position >= size)
1352:                            position = size - 1;
1353:
1354:                        innerscan:
1355:                        // go from position (the last cached item in the holder array
1356:                        // to 0 (the first).  Otherwise, if we go from 0 to
1357:                        // position, some other thread may come in and shrink items
1358:                        // which are between 0 and position.  Since a shrink moves all
1359:                        // items up, we may skip some items without cleaning.
1360:                        for (; position >= 0; position--, item = null) {
1361:
1362:                            item = (CachedItem) holders.get(position);
1363:
1364:                            if (!item.isValid())
1365:                                continue innerscan;
1366:
1367:                            if (!item.getEntry().isDirty())
1368:                                continue innerscan;
1369:
1370:                            if (partialKey != null) {
1371:
1372:                                Object key = item.getEntry().getIdentity();
1373:
1374:                                if (!partialKey.match(key))
1375:                                    continue;
1376:                            }
1377:
1378:                            item.keepForClean();
1379:                            break innerscan;
1380:                        }
1381:                    } // end of synchronized block
1382:
1383:                    if (position < 0) {
1384:                        return;
1385:                    }
1386:
1387:                    try {
1388:
1389:                        item.clean(false);
1390:                    } finally {
1391:                        release(item);
1392:                    }
1393:                    position--;
1394:
1395:                } // for (;;)
1396:            }
1397:
1398:            protected long shrinkSize(long currentSize) {
1399:
1400:                long maxSize = getMaximumSize();
1401:
1402:                long toShrink = currentSize - maxSize;
1403:                if (toShrink <= 0)
1404:                    return 0;
1405:
1406:                // only shrink 10% of the maximum size
1407:                long shrinkLimit = maxSize / 10;
1408:                if (shrinkLimit == 0)
1409:                    shrinkLimit = 2;
1410:
1411:                if (toShrink < shrinkLimit)
1412:                    return toShrink;
1413:                else
1414:                    return shrinkLimit;
1415:            }
1416:
1417:            /**
1418:            	The background cleaner tries to make sure that there are serveral
1419:            	cleaned or invalied buffers ahead of the clock hand so that when they
1420:            	are evicted, they don't need to be cleaned.
1421:
1422:            	The way this routine work is as follows, starting at the current clock
1423:            	hand position, go forward around the cache buffers, moving the same
1424:            	route that the clock hand moves.  It keep tracks of the number of
1425:            	invalid or not recently used buffers it sees along the way.  If it sees
1426:            	a not recently used buffer, it will clean it.  After it has seen N
1427:            	invalid or not recently used buffers, or it has gone around and visited
1428:            	all buffers in the cache, it finished.
1429:
1430:            	It does not clean recently used buffers.
1431:
1432:            	<P>MT - must be MT-safe.  It takes a snapshot of the current clock hand
1433:            	position (a synchronous call).  Getting and looking at the next
1434:            	serveral cached item is synchronized on this (RESOLVE: probably doesn't
1435:            	need to be).  Cleaning of the cacheable is handle by the cacheable itself.
1436:
1437:             */
1438:            protected int performWork(boolean shrinkOnly) {
1439:                long target;
1440:                long toShrink;
1441:                int maxLooks;
1442:
1443:                synchronized (this ) {
1444:                    if (!active) {
1445:                        needService = false;
1446:                        return Serviceable.DONE;
1447:                    } else {
1448:                        long currentSize = getCurrentSize();
1449:                        target = currentSize / 20; // attempt to get 5% of the cache clean
1450:                        toShrink = wokenToClean ? 0 : shrinkSize(currentSize);
1451:                    }
1452:
1453:                    if (target == 0) {
1454:                        wokenToClean = false;
1455:                        needService = false;
1456:                        return Serviceable.DONE;
1457:                    }
1458:
1459:                    if (!wokenToClean && (toShrink <= 0)) {
1460:                        needService = false;
1461:                        return Serviceable.DONE;
1462:                    }
1463:
1464:                    maxLooks = useByteCount ? (holders.size() / 10)
1465:                            : (int) (target * 2);
1466:                }
1467:
1468:                // try to clean the next N (target) cached item, 
1469:                long clean = 0;
1470:                int cleaned = 0; // only used in debug
1471:                CachedItem item = null;
1472:                int currentPosition = 0;
1473:
1474:                String ThreadName = null;
1475:
1476:                if (SanityManager.DEBUG) {
1477:                    if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) {
1478:                        ThreadName = Thread.currentThread().getName();
1479:                        SanityManager.DEBUG(DaemonService.DaemonTrace,
1480:                                ThreadName + " Cleaning " + name
1481:                                        + " clientNumber " + myClientNumber);
1482:                    }
1483:                }
1484:
1485:                synchronized (this ) {
1486:                    int itemCount = holders.size();
1487:                    currentPosition = clockHand;
1488:
1489:                    // see if the cache needs to shrink
1490:                    boolean shrunk = false;
1491:                    long currentSize = getCurrentSize();
1492:
1493:                    for (; shrinkOnly ? (currentSize > maximumSize && toShrink > 0)
1494:                            : (clean < target); item = null) {
1495:                        if (++currentPosition >= itemCount) {
1496:                            if (itemCount == 0)
1497:                                break;
1498:
1499:                            currentPosition = 0;
1500:
1501:                        }
1502:
1503:                        if (maxLooks-- <= 0) {
1504:                            if (SanityManager.DEBUG) {
1505:                                if (SanityManager
1506:                                        .DEBUG_ON(DaemonService.DaemonTrace)) {
1507:                                    SanityManager.DEBUG(
1508:                                            DaemonService.DaemonTrace,
1509:                                            ThreadName + " done one round of "
1510:                                                    + name);
1511:                                }
1512:                            }
1513:
1514:                            break; // done one round
1515:                        }
1516:
1517:                        item = (CachedItem) holders.get(currentPosition);
1518:
1519:                        if (item.isKept())
1520:                            continue;
1521:
1522:                        if (!item.isValid()) {
1523:                            if (toShrink > 0) {
1524:
1525:                                if (SanityManager.DEBUG) {
1526:                                    if (SanityManager
1527:                                            .DEBUG_ON(ClockFactory.CacheTrace)) {
1528:                                        SanityManager.DEBUG(
1529:                                                ClockFactory.CacheTrace, name
1530:                                                        + " shrinking item "
1531:                                                        + item
1532:                                                        + " at position "
1533:                                                        + currentPosition);
1534:                                    }
1535:                                }
1536:
1537:                                toShrink -= currentSize;
1538:                                holders.remove(currentPosition);
1539:                                if (useByteCount)
1540:                                    currentByteCount -= getItemSize(item);
1541:                                currentSize = getCurrentSize();
1542:                                toShrink += currentSize;
1543:                                itemCount--;
1544:
1545:                                // account for the fact all the items have shifted down
1546:                                currentPosition--;
1547:
1548:                                shrunk = true;
1549:                            }
1550:                            continue;
1551:                        }
1552:
1553:                        if (item.recentlyUsed())
1554:                            continue;
1555:
1556:                        // found a valid, not kept, and not recently used item
1557:                        // this item will be cleaned
1558:                        int itemSize = getItemSize(item);
1559:                        clean += itemSize;
1560:                        if (!item.getEntry().isDirty()) {
1561:
1562:                            if (toShrink > 0) {
1563:                                if (SanityManager.DEBUG) {
1564:                                    if (SanityManager
1565:                                            .DEBUG_ON(ClockFactory.CacheTrace)) {
1566:                                        SanityManager.DEBUG(
1567:                                                ClockFactory.CacheTrace, name
1568:                                                        + " shrinking item "
1569:                                                        + item
1570:                                                        + " at position "
1571:                                                        + currentPosition);
1572:                                    }
1573:                                }
1574:
1575:                                toShrink -= currentSize;
1576:                                removeIdentity(item);
1577:                                holders.remove(currentPosition);
1578:                                if (useByteCount)
1579:                                    currentByteCount -= getItemSize(item);
1580:                                currentSize = getCurrentSize();
1581:                                toShrink += currentSize;
1582:                                itemCount--;
1583:                                shrunk = true;
1584:
1585:                                // account for the fact all the items have shifted down
1586:                                currentPosition--;
1587:                            }
1588:                            continue;
1589:                        }
1590:
1591:                        if (shrinkOnly)
1592:                            continue;
1593:
1594:                        // found one that needs cleaning, keep it to clean
1595:                        item.keepForClean();
1596:                        break;
1597:                    } // end of for loop
1598:
1599:                    if (shrunk)
1600:                        trimToSize();
1601:
1602:                    if (item == null) {
1603:                        wokenToClean = false;
1604:                        needService = false;
1605:                        return Serviceable.DONE;
1606:                    }
1607:                } // end of sync block
1608:
1609:                try {
1610:                    if (SanityManager.DEBUG) {
1611:                        if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) {
1612:                            SanityManager.DEBUG(DaemonService.DaemonTrace,
1613:                                    ThreadName + " cleaning entry  in " + name);
1614:                        }
1615:                    }
1616:
1617:                    item.clean(false);
1618:                    if (SanityManager.DEBUG) // only need stats for debug
1619:                        cleaned++;
1620:
1621:                } catch (StandardException se) {
1622:                    // RESOLVE - should probably throw the error into the log.
1623:                } finally {
1624:                    release(item);
1625:                    item = null;
1626:                }
1627:
1628:                if (SanityManager.DEBUG) {
1629:                    if (SanityManager.DEBUG_ON(DaemonService.DaemonTrace)) {
1630:                        SanityManager.DEBUG(DaemonService.DaemonTrace,
1631:                                ThreadName + " Found " + clean
1632:                                        + " clean items, cleaned " + cleaned
1633:                                        + " items in " + name);
1634:                    }
1635:                }
1636:
1637:                needService = true;
1638:                return Serviceable.REQUEUE; // return is actually ignored.
1639:            } // end of performWork
1640:
1641:            private int getItemSize(CachedItem item) {
1642:                if (!useByteCount)
1643:                    return 1;
1644:                SizedCacheable entry = (SizedCacheable) item.getEntry();
1645:                if (null == entry)
1646:                    return 0;
1647:                return entry.getSize();
1648:            } // end of getItemSize
1649:
1650:            /**
1651:            	Return statistics about cache that may be implemented.
1652:             **/
1653:            public synchronized long[] getCacheStats() {
1654:                stat.currentSize = getCurrentSize();
1655:                return stat.getStats();
1656:            }
1657:
1658:            /**
1659:            	Reset the statistics to 0.
1660:             **/
1661:            public void resetCacheStats() {
1662:                stat.reset();
1663:            }
1664:
1665:            /**
1666:             * @return the current maximum size of the cache.
1667:             */
1668:            public synchronized long getMaximumSize() {
1669:                return maximumSize;
1670:            }
1671:
1672:            /**
1673:             * Change the maximum size of the cache. If the size is decreased then cache entries
1674:             * will be thrown out.
1675:             *
1676:             * @param newSize the new maximum cache size
1677:             *
1678:             * @exception StandardException Cloudscape Standard error policy
1679:             */
1680:            public void resize(long newSize) throws StandardException {
1681:                boolean shrink;
1682:
1683:                synchronized (this ) {
1684:                    maximumSize = newSize;
1685:                    stat.maxSize = maximumSize;
1686:                    shrink = (shrinkSize(getCurrentSize()) > 0);
1687:                }
1688:                if (shrink) {
1689:                    performWork(true /* shrink only */);
1690:                    /* performWork does not remove recently used entries nor does it mark them as
1691:                     * not recently used. Therefore if the cache has not shrunk enough we will call rotateClock
1692:                     * to free up some entries.
1693:                     */
1694:                    if (shrinkSize(getCurrentSize()) > 0) {
1695:                        CachedItem freeItem = rotateClock((float) 2.0);
1696:                        /* rotateClock(2.0) means that the clock will rotate through the cache as much as
1697:                         * twice.  If it does not find sufficient unused items the first time through it
1698:                         * will almost certainly find enough of them the second time through, because it
1699:                         * marked all the items as not recently used in the first pass.
1700:                         *
1701:                         * If the cache is very heavily used by other threads then a lot of the items marked as
1702:                         * unused in the first pass may be used before rotateClock passes over them again. In this
1703:                         * unlikely case rotateClock( 2.0) may not be able to clear out enough space to bring the
1704:                         * current size down to the maximum. However the cache size should come down as rotateClock
1705:                         * is called in the normal course of operation.
1706:                         */
1707:                        if (freeItem != null)
1708:                            freeItem.unkeepForCreate();
1709:                    }
1710:                }
1711:
1712:            } // end of resize;
1713:
1714:            private synchronized long getCurrentSize() {
1715:                if (!useByteCount)
1716:                    return holders.size();
1717:                return currentByteCount + holders.size() * ITEM_OVERHEAD;
1718:            }
1719:
1720:            /**
1721:             * Perform an operation on (approximately) all entries that matches the filter,
1722:             * or all entries if the filter is null.  Entries that are added while the
1723:             * cache is being scanned might or might not be missed.
1724:             *
1725:             * @param filter
1726:             * @param operator
1727:             */
1728:            public void scan(Matchable filter, Operator operator) {
1729:                int itemCount = 1;
1730:                Cacheable entry = null;
1731:                CachedItem item = null;
1732:
1733:                // Do not call the operator while holding the synchronization lock.
1734:                // However we cannot access an item's links without holding the synchronization lock,
1735:                // nor can we assume that an item is still in the cache unless we hold the synchronization
1736:                // lock or the item is marked as kept.
1737:                for (int position = 0;; position++) {
1738:                    synchronized (this ) {
1739:                        if (null != item) {
1740:                            release(item);
1741:                            item = null;
1742:                        }
1743:
1744:                        for (; position < holders.size(); position++) {
1745:                            item = (CachedItem) holders.get(position);
1746:                            if (null != item) {
1747:                                try {
1748:                                    entry = item.use();
1749:                                } catch (StandardException se) {
1750:                                    continue;
1751:                                }
1752:
1753:                                if (null != entry
1754:                                        && (null == filter || filter
1755:                                                .match(entry))) {
1756:                                    item.keepForClean();
1757:                                    break;
1758:                                }
1759:                            }
1760:                        }
1761:                        if (position >= holders.size())
1762:                            return;
1763:
1764:                    } // end of synchronization
1765:                    operator.operate(entry);
1766:                    // Do not release the item until we have re-acquired the synchronization lock.
1767:                    // Otherwise the item may be removed and its next link invalidated.
1768:                }
1769:            } // end of scan
1770:
1771:            private int trimRequests = 0;
1772:
1773:            /* Trim out invalid items from holders if there are a lot of them. This is expensive if
1774:             * holders is large.
1775:             * The caller must hold the cache synchronization lock.
1776:             */
1777:            private void trimToSize() {
1778:                int size = holders.size();
1779:
1780:                // Trimming is expensive, don't do it often.
1781:                trimRequests++;
1782:                if (trimRequests < size / 8)
1783:                    return;
1784:                trimRequests = 0;
1785:
1786:                // move invalid items to the end.
1787:                int endPosition = size - 1;
1788:
1789:                int invalidCount = 0;
1790:                for (int i = 0; i <= endPosition; i++) {
1791:                    CachedItem item = (CachedItem) holders.get(i);
1792:
1793:                    if (item.isKept())
1794:                        continue;
1795:
1796:                    if (item.isValid())
1797:                        continue;
1798:
1799:                    invalidCount++;
1800:
1801:                    // swap with an item later in the list
1802:                    // try to keep free items at the end of the holders array.
1803:                    for (; endPosition > i; endPosition--) {
1804:                        CachedItem last = (CachedItem) holders.get(endPosition);
1805:                        if (last.isValid()) {
1806:                            holders.set(i, last);
1807:                            holders.set(endPosition, item);
1808:                            endPosition--;
1809:                            break;
1810:                        }
1811:                    }
1812:                }
1813:                // small cache - don't shrink.
1814:                if (size < 32)
1815:                    return;
1816:
1817:                // now decide if we need to shrink the holder array or not.
1818:                int validItems = size - invalidCount;
1819:
1820:                // over 75% entries used, don't shrink.
1821:                if (validItems > ((3 * size) / 4))
1822:                    return;
1823:
1824:                // keep 10% new items.
1825:                int newSize = validItems + (validItems / 10);
1826:
1827:                if (newSize >= size)
1828:                    return;
1829:
1830:                // remove items, starting at the end,  where
1831:                // hopefully most of the free items are.
1832:                for (int r = size - 1; r > newSize; r--) {
1833:                    CachedItem remove = (CachedItem) holders.get(r);
1834:                    if (remove.isKept() || remove.isValid()) {
1835:                        continue;
1836:                    }
1837:
1838:                    if (useByteCount) {
1839:                        currentByteCount -= getItemSize(remove);
1840:                    }
1841:
1842:                    holders.remove(r);
1843:                }
1844:
1845:                holders.trimToSize();
1846:                // move the clock hand to the start of the invalid items.
1847:                clockHand = validItems + 1;
1848:
1849:            } // end of trimToSize
1850:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.