1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.cache.CachedItem |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.services.cache; |
22 | |
23 | import org.apache.derby.iapi.services.cache.Cacheable; |
24 | import org.apache.derby.iapi.services.cache.CacheableFactory; |
25 | import org.apache.derby.iapi.services.cache.CacheManager; |
26 | import org.apache.derby.iapi.error.StandardException; |
27 | |
28 | import org.apache.derby.iapi.services.sanity.SanityManager; |
29 | import org.apache.derby.iapi.services.context.ContextService; |
30 | |
31 | /** |
32 | A generic class to represent the cache related infomation of a cached object (Cacheable). |
33 | <P><PRE> |
34 | The relationship between isValid and settingIdentity can be explain by the |
35 | following life cycle of a cached item. |
36 | |
37 | Stage 1 2 3 |
38 | ---------------------- |
39 | isValid F T T |
40 | settingIdentity X T F |
41 | |
42 | In Stage 1, the CachedItem is created but it is invalid and has an entry |
43 | that is just a holder object with no identity. |
44 | |
45 | In Stage 2, the identity has been set and the item is being created or |
46 | being faulted into the cache. |
47 | |
48 | In Stage 3, the item found in the CachedItem entry |
49 | </PRE> <P> |
50 | Remove is set if this item is being removed out of existance, not just |
51 | being evicted from the cache. When the last referece to it release it from |
52 | the cache, it will be removed. |
53 | <BR> |
54 | RecentlyUsed is set whenever this item is accessed (via a keep() call). |
55 | It is reset by the clockHand as it sweeps around the cache looking for |
56 | victims to evict. |
57 | |
58 | <P>MT - must be MT-safe and work with cache manager. Every method that |
59 | access (set or get) instance variables is synchronized on the cached item |
60 | object. The following method waits so it should not be called by the cache |
61 | manager inside a sync block: clean(), waitFor(), create(), remove(). |
62 | (RESOLVE: need to move these from the cache manager to here) |
63 | |
64 | @see org.apache.derby.impl.services.cache |
65 | @see Cacheable |
66 | */ |
67 | public final class CachedItem { |
68 | |
69 | private static final int VALID = 0x00000001; |
70 | private static final int REMOVE_REQUESTED = 0x00000002; |
71 | private static final int SETTING_IDENTITY = 0x00000004; |
72 | private static final int REMOVE_OK = 0x00000008; |
73 | |
74 | private static final int RECENTLY_USED = 0x00000010; |
75 | |
76 | /* |
77 | ** Fields |
78 | */ |
79 | |
80 | /** |
81 | Does entry (the Cacheable) have an identity. |
82 | |
83 | <BR> MT - single thread required : synchronization provided by cache manager. |
84 | */ |
85 | private int state; |
86 | |
87 | /** |
88 | The current keep count on the entry. |
89 | |
90 | <BR> MT - single thread required : synchronization provided by cache manager. |
91 | |
92 | */ |
93 | private int keepCount; |
94 | |
95 | /** |
96 | The Cacheable object being represented. |
97 | |
98 | <BR> Mutable - content dynamic |
99 | */ |
100 | private Cacheable entry; |
101 | |
102 | /** |
103 | Create a CachedItem in the not valid state. |
104 | */ |
105 | public CachedItem() { |
106 | } |
107 | |
108 | /** |
109 | Keep the cached object after a search. |
110 | |
111 | */ |
112 | public void keepAfterSearch() { |
113 | keepCount++; |
114 | setUsed(true); |
115 | } |
116 | |
117 | public void keepForCreate() { |
118 | if (SanityManager.DEBUG) { |
119 | SanityManager.ASSERT(!isKept()); |
120 | SanityManager.ASSERT(!isValid()); |
121 | } |
122 | keepCount = 1; |
123 | state |= SETTING_IDENTITY; |
124 | } |
125 | |
126 | public void unkeepForCreate( ) |
127 | { |
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 | |
141 | /** |
142 | Unkeep the cached object. |
143 | |
144 | <P>MT - not synchronized, only modified single threaded by the cache manager |
145 | |
146 | @return if the object is still kept after this call. |
147 | */ |
148 | public synchronized boolean unkeep() { |
149 | boolean unkept = --keepCount == 0; |
150 | |
151 | if (SanityManager.DEBUG) { |
152 | SanityManager.ASSERT(keepCount >= 0); |
153 | } |
154 | return unkept && ((state & REMOVE_REQUESTED) != 0); |
155 | } |
156 | |
157 | /** |
158 | Is the cached object kept? |
159 | |
160 | <P>MT - not synchronized, only accessed single threaded by the cache manager |
161 | */ |
162 | public final boolean isKept() { |
163 | |
164 | return keepCount != 0; |
165 | } |
166 | |
167 | /** |
168 | Clean the cached object |
169 | |
170 | <P>MT - <BR> |
171 | The wait will not release the lock on the cache manager, so the |
172 | cache manager should not waitfor clean inside a sync block or |
173 | the whole cache will freeze |
174 | |
175 | @param forRemove if true, get rid of the backend persistent store object |
176 | @exception StandardException error thrown while writing cacheable |
177 | object to disk |
178 | */ |
179 | public void clean(boolean forRemove) throws StandardException |
180 | { |
181 | entry.clean(forRemove); |
182 | } |
183 | |
184 | /** |
185 | Set the state of the to-be removed flag. |
186 | */ |
187 | public synchronized void setRemoveState() { |
188 | state |= REMOVE_REQUESTED; |
189 | } |
190 | |
191 | /** |
192 | Does the cached object have a valid identity. |
193 | */ |
194 | public final synchronized boolean isValid() { |
195 | return (state & VALID) != 0; |
196 | } |
197 | |
198 | /** |
199 | Set the valid state of the cached object. |
200 | */ |
201 | public synchronized void setValidState(boolean flag) { |
202 | |
203 | if (flag) |
204 | state |= VALID; |
205 | else |
206 | state &= ~VALID; |
207 | |
208 | state &= ~(REMOVE_REQUESTED | REMOVE_OK); |
209 | |
210 | setUsed(flag); |
211 | } |
212 | |
213 | /** |
214 | Get the cached object. |
215 | */ |
216 | public Cacheable getEntry() { |
217 | return entry; |
218 | } |
219 | |
220 | /** |
221 | Make entry (the Cacheable) take on a new identity. |
222 | */ |
223 | public Cacheable takeOnIdentity(CacheManager cm, CacheableFactory holderFactory, |
224 | Object key, boolean forCreate, Object createParameter) |
225 | throws StandardException { |
226 | |
227 | // tell the object it needs to create itself |
228 | Cacheable oldEntry = entry; |
229 | if (oldEntry == null) |
230 | oldEntry = holderFactory.newCacheable(cm); |
231 | |
232 | if (forCreate) { |
233 | entry = oldEntry.createIdentity(key, createParameter); |
234 | } else { |
235 | entry = oldEntry.setIdentity(key); |
236 | } |
237 | |
238 | if (entry != null) { |
239 | // item was found or created |
240 | if (SanityManager.DEBUG) { |
241 | SanityManager.ASSERT(entry.getIdentity().equals(key)); |
242 | } |
243 | |
244 | return entry; |
245 | } |
246 | |
247 | entry = oldEntry; |
248 | return null; |
249 | } |
250 | |
251 | public synchronized void settingIdentityComplete() { |
252 | // notify all waiters that this item has finished setting its identity, |
253 | // successfully or not. |
254 | state &= ~SETTING_IDENTITY; |
255 | |
256 | notifyAll(); |
257 | } |
258 | |
259 | /** |
260 | Allow use of the cacheable entry. |
261 | */ |
262 | |
263 | public synchronized Cacheable use() throws StandardException { |
264 | |
265 | while ((state & SETTING_IDENTITY) != 0) { |
266 | try { |
267 | if (SanityManager.DEBUG) { |
268 | SanityManager.DEBUG("CacheTrace", |
269 | "trying to use a cached item that is taking on an identity"); |
270 | } |
271 | |
272 | wait(); |
273 | |
274 | } catch (InterruptedException ie) { |
275 | throw StandardException.interrupt(ie); |
276 | } |
277 | } |
278 | |
279 | // see if the setting of this identity failed ... |
280 | if (!isValid()) |
281 | return null; |
282 | |
283 | if (SanityManager.DEBUG) |
284 | { |
285 | if (SanityManager.DEBUG_ON("CacheTrace")) |
286 | SanityManager.DEBUG( |
287 | "CacheTrace", "item keep count is " + keepCount); |
288 | } |
289 | |
290 | |
291 | return entry; |
292 | } |
293 | |
294 | /** |
295 | */ |
296 | public void remove(boolean removeNow) throws StandardException { |
297 | |
298 | if (!removeNow) { |
299 | |
300 | synchronized (this) { |
301 | while ((state & REMOVE_OK) == 0) { |
302 | try { |
303 | wait(); |
304 | } catch (InterruptedException ie) { |
305 | throw StandardException.interrupt(ie); |
306 | } |
307 | } |
308 | } |
309 | } |
310 | |
311 | clean(true); |
312 | } |
313 | |
314 | public synchronized void notifyRemover() { |
315 | |
316 | if (SanityManager.DEBUG) { |
317 | SanityManager.ASSERT((state & REMOVE_REQUESTED) != 0); |
318 | SanityManager.ASSERT(isKept()); |
319 | } |
320 | |
321 | state |= REMOVE_OK; |
322 | notifyAll(); |
323 | } |
324 | |
325 | /** |
326 | The clock hand has swept past this entry. |
327 | */ |
328 | public synchronized void setUsed(boolean flag) |
329 | { |
330 | if (flag) |
331 | state |= RECENTLY_USED; |
332 | else |
333 | state &= ~RECENTLY_USED; |
334 | } |
335 | |
336 | /** |
337 | Has the cached object been referenced (kept) since the last sweep of |
338 | the clock hand? |
339 | */ |
340 | public synchronized boolean recentlyUsed() { |
341 | return (state & RECENTLY_USED) != 0; |
342 | } |
343 | } |
344 | |
345 | |
346 | |