001: /*
002:
003: Derby - Class org.apache.derby.impl.services.cache.CachedItem
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.services.cache;
023:
024: import org.apache.derby.iapi.services.cache.Cacheable;
025: import org.apache.derby.iapi.services.cache.CacheableFactory;
026: import org.apache.derby.iapi.services.cache.CacheManager;
027: import org.apache.derby.iapi.error.StandardException;
028:
029: import org.apache.derby.iapi.services.sanity.SanityManager;
030: import org.apache.derby.iapi.services.context.ContextService;
031:
032: /**
033: A generic class to represent the cache related infomation of a cached object (Cacheable).
034: <P><PRE>
035: The relationship between isValid and settingIdentity can be explain by the
036: following life cycle of a cached item.
037:
038: Stage 1 2 3
039: ----------------------
040: isValid F T T
041: settingIdentity X T F
042:
043: In Stage 1, the CachedItem is created but it is invalid and has an entry
044: that is just a holder object with no identity.
045:
046: In Stage 2, the identity has been set and the item is being created or
047: being faulted into the cache.
048:
049: In Stage 3, the item found in the CachedItem entry
050: </PRE> <P>
051: Remove is set if this item is being removed out of existance, not just
052: being evicted from the cache. When the last referece to it release it from
053: the cache, it will be removed.
054: <BR>
055: RecentlyUsed is set whenever this item is accessed (via a keep() call).
056: It is reset by the clockHand as it sweeps around the cache looking for
057: victims to evict.
058:
059: <P>MT - must be MT-safe and work with cache manager. Every method that
060: access (set or get) instance variables is synchronized on the cached item
061: object. The following method waits so it should not be called by the cache
062: manager inside a sync block: clean(), waitFor(), create(), remove().
063: (RESOLVE: need to move these from the cache manager to here)
064:
065: @see org.apache.derby.impl.services.cache
066: @see Cacheable
067: */
068: public final class CachedItem {
069:
070: private static final int VALID = 0x00000001;
071: private static final int REMOVE_REQUESTED = 0x00000002;
072: private static final int SETTING_IDENTITY = 0x00000004;
073: private static final int REMOVE_OK = 0x00000008;
074:
075: private static final int RECENTLY_USED = 0x00000010;
076:
077: /*
078: ** Fields
079: */
080:
081: /**
082: Does entry (the Cacheable) have an identity.
083:
084: <BR> MT - single thread required : synchronization provided by cache manager.
085: */
086: private int state;
087:
088: /**
089: The current keep count on the entry.
090:
091: <BR> MT - single thread required : synchronization provided by cache manager.
092:
093: */
094: private int keepCount;
095:
096: /**
097: The Cacheable object being represented.
098:
099: <BR> Mutable - content dynamic
100: */
101: private Cacheable entry;
102:
103: /**
104: Create a CachedItem in the not valid state.
105: */
106: public CachedItem() {
107: }
108:
109: /**
110: Keep the cached object after a search.
111:
112: */
113: public void keepAfterSearch() {
114: keepCount++;
115: setUsed(true);
116: }
117:
118: public void keepForCreate() {
119: if (SanityManager.DEBUG) {
120: SanityManager.ASSERT(!isKept());
121: SanityManager.ASSERT(!isValid());
122: }
123: keepCount = 1;
124: state |= SETTING_IDENTITY;
125: }
126:
127: public void unkeepForCreate() {
128: settingIdentityComplete();
129: unkeep();
130: }
131:
132: public void keepForClean() {
133: if (SanityManager.DEBUG) {
134: SanityManager.ASSERT(isValid());
135: }
136: keepCount++;
137: }
138:
139: /**
140: Unkeep the cached object.
141:
142: <P>MT - not synchronized, only modified single threaded by the cache manager
143:
144: @return if the object is still kept after this call.
145: */
146: public synchronized boolean unkeep() {
147: boolean unkept = --keepCount == 0;
148:
149: if (SanityManager.DEBUG) {
150: SanityManager.ASSERT(keepCount >= 0);
151: }
152: return unkept && ((state & REMOVE_REQUESTED) != 0);
153: }
154:
155: /**
156: Is the cached object kept?
157:
158: <P>MT - not synchronized, only accessed single threaded by the cache manager
159: */
160: public final boolean isKept() {
161:
162: return keepCount != 0;
163: }
164:
165: /**
166: Clean the cached object
167:
168: <P>MT - <BR>
169: The wait will not release the lock on the cache manager, so the
170: cache manager should not waitfor clean inside a sync block or
171: the whole cache will freeze
172:
173: @param forRemove if true, get rid of the backend persistent store object
174: @exception StandardException error thrown while writing cacheable
175: object to disk
176: */
177: public void clean(boolean forRemove) throws StandardException {
178: entry.clean(forRemove);
179: }
180:
181: /**
182: Set the state of the to-be removed flag.
183: */
184: public synchronized void setRemoveState() {
185: state |= REMOVE_REQUESTED;
186: }
187:
188: /**
189: Does the cached object have a valid identity.
190: */
191: public final synchronized boolean isValid() {
192: return (state & VALID) != 0;
193: }
194:
195: /**
196: Set the valid state of the cached object.
197: */
198: public synchronized void setValidState(boolean flag) {
199:
200: if (flag)
201: state |= VALID;
202: else
203: state &= ~VALID;
204:
205: state &= ~(REMOVE_REQUESTED | REMOVE_OK);
206:
207: setUsed(flag);
208: }
209:
210: /**
211: Get the cached object.
212: */
213: public Cacheable getEntry() {
214: return entry;
215: }
216:
217: /**
218: Make entry (the Cacheable) take on a new identity.
219: */
220: public Cacheable takeOnIdentity(CacheManager cm,
221: CacheableFactory holderFactory, Object key,
222: boolean forCreate, Object createParameter)
223: throws StandardException {
224:
225: // tell the object it needs to create itself
226: Cacheable oldEntry = entry;
227: if (oldEntry == null)
228: oldEntry = holderFactory.newCacheable(cm);
229:
230: if (forCreate) {
231: entry = oldEntry.createIdentity(key, createParameter);
232: } else {
233: entry = oldEntry.setIdentity(key);
234: }
235:
236: if (entry != null) {
237: // item was found or created
238: if (SanityManager.DEBUG) {
239: SanityManager.ASSERT(entry.getIdentity().equals(key));
240: }
241:
242: return entry;
243: }
244:
245: entry = oldEntry;
246: return null;
247: }
248:
249: public synchronized void settingIdentityComplete() {
250: // notify all waiters that this item has finished setting its identity,
251: // successfully or not.
252: state &= ~SETTING_IDENTITY;
253:
254: notifyAll();
255: }
256:
257: /**
258: Allow use of the cacheable entry.
259: */
260:
261: public synchronized Cacheable use() throws StandardException {
262:
263: while ((state & SETTING_IDENTITY) != 0) {
264: try {
265: if (SanityManager.DEBUG) {
266: SanityManager
267: .DEBUG("CacheTrace",
268: "trying to use a cached item that is taking on an identity");
269: }
270:
271: wait();
272:
273: } catch (InterruptedException ie) {
274: throw StandardException.interrupt(ie);
275: }
276: }
277:
278: // see if the setting of this identity failed ...
279: if (!isValid())
280: return null;
281:
282: if (SanityManager.DEBUG) {
283: if (SanityManager.DEBUG_ON("CacheTrace"))
284: SanityManager.DEBUG("CacheTrace", "item keep count is "
285: + keepCount);
286: }
287:
288: return entry;
289: }
290:
291: /**
292: */
293: public void remove(boolean removeNow) throws StandardException {
294:
295: if (!removeNow) {
296:
297: synchronized (this ) {
298: while ((state & REMOVE_OK) == 0) {
299: try {
300: wait();
301: } catch (InterruptedException ie) {
302: throw StandardException.interrupt(ie);
303: }
304: }
305: }
306: }
307:
308: clean(true);
309: }
310:
311: public synchronized void notifyRemover() {
312:
313: if (SanityManager.DEBUG) {
314: SanityManager.ASSERT((state & REMOVE_REQUESTED) != 0);
315: SanityManager.ASSERT(isKept());
316: }
317:
318: state |= REMOVE_OK;
319: notifyAll();
320: }
321:
322: /**
323: The clock hand has swept past this entry.
324: */
325: public synchronized void setUsed(boolean flag) {
326: if (flag)
327: state |= RECENTLY_USED;
328: else
329: state &= ~RECENTLY_USED;
330: }
331:
332: /**
333: Has the cached object been referenced (kept) since the last sweep of
334: the clock hand?
335: */
336: public synchronized boolean recentlyUsed() {
337: return (state & RECENTLY_USED) != 0;
338: }
339: }
|