001package org.cache2k.impl; 002 003/* 004 * #%L 005 * cache2k core package 006 * %% 007 * Copyright (C) 2000 - 2015 headissue GmbH, Munich 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import org.cache2k.MutableCacheEntry; 026import org.cache2k.storage.StorageEntry; 027 028/** 029 * The cache entry. This is a combined hashtable entry with hashCode and 030 * and collision list (other field) and it contains a double linked list 031 * (next and previous) for the eviction algorithm. 032 * 033 * @author Jens Wilke 034 */ 035@SuppressWarnings("unchecked") 036public class Entry<E extends Entry, K, T> 037 implements MutableCacheEntry<K,T>, StorageEntry { 038 039 static final int FETCHED_STATE = 16; 040 static final int REFRESH_STATE = FETCHED_STATE + 1; 041 static final int REPUT_STATE = FETCHED_STATE + 3; 042 043 static final int FETCH_IN_PROGRESS_VALID = FETCHED_STATE + 4; 044 045 static final int LOADED_NON_VALID_AND_PUT = 9; 046 047 static final int FETCH_ABORT = 8; 048 049 static final int FETCH_IN_PROGRESS_NON_VALID = 7; 050 051 /** Storage was checked, no data available */ 052 static final int LOADED_NON_VALID_AND_FETCH = 6; 053 054 /** Storage was checked, no data available */ 055 static final int LOADED_NON_VALID = 5; 056 057 static final int EXPIRED_STATE = 4; 058 059 /** Logically the same as immediately expired */ 060 static final int FETCH_NEXT_TIME_STATE = 3; 061 062 static private final int REMOVED_STATE = 2; 063 064 static private final int FETCH_IN_PROGRESS_VIRGIN = 1; 065 066 static final int VIRGIN_STATE = 0; 067 068 static final int EXPIRY_TIME_MIN = 32; 069 070 static private final StaleMarker STALE_MARKER_KEY = new StaleMarker(); 071 072 final static InitialValueInEntryNeverReturned INITIAL_VALUE = new InitialValueInEntryNeverReturned(); 073 074 public BaseCache.MyTimerTask task; 075 076 /** 077 * Time the entry was last updated by put or by fetching it from the cache source. 078 * The time is the time in millis times 2. A set bit 1 means the entry is fetched from 079 * the storage and not modified since then. 080 */ 081 public long fetchedTime; 082 083 /** 084 * Contains the next time a refresh has to occur. Low values have a special meaning, see defined constants. 085 * Negative values means the refresh time was expired, and we need to check the time. 086 */ 087 public volatile long nextRefreshTime; 088 089 public K key; 090 091 public volatile T value = (T) INITIAL_VALUE; 092 093 /** 094 * Hash implementation: the calculated, modified hash code, retrieved from the key when the entry is 095 * inserted in the cache 096 * 097 * @see BaseCache#modifiedHash(int) 098 */ 099 public int hashCode; 100 101 /** 102 * Hash implementation: Link to another entry in the same hash table slot when the hash code collides. 103 */ 104 public Entry<E, K, T> another; 105 106 /** Lru list: pointer to next element or list head */ 107 public E next; 108 /** Lru list: pointer to previous element or list head */ 109 public E prev; 110 111 public void setLastModification(long t) { 112 fetchedTime = t << 1; 113 } 114 115 /** 116 * Memory entry needs to be send to the storage. 117 */ 118 public boolean isDirty() { 119 return (fetchedTime & 1) == 0; 120 } 121 122 public void setLastModificationFromStorage(long t) { 123 fetchedTime = t << 1 | 1; 124 } 125 126 public void resetDirty() { 127 fetchedTime = fetchedTime | 1; 128 } 129 130 /** Reset next as a marker for {@link #isRemovedFromReplacementList()} */ 131 public final void removedFromList() { 132 next = null; 133 } 134 135 /** Check that this entry is removed from the list, may be used in assertions. */ 136 public boolean isRemovedFromReplacementList() { 137 return isStale () || next == null; 138 } 139 140 public E shortCircuit() { 141 return next = prev = (E) this; 142 } 143 144 public final boolean isVirgin() { 145 return 146 nextRefreshTime == VIRGIN_STATE || 147 nextRefreshTime == FETCH_IN_PROGRESS_VIRGIN; 148 } 149 150 public final boolean isFetchNextTimeState() { 151 return nextRefreshTime == FETCH_NEXT_TIME_STATE; 152 } 153 154 /** 155 * The entry value was fetched and is valid, which means it can be 156 * returned by the cache. If a valid entry gets removed from the 157 * cache the data is still valid. This is because a concurrent get needs to 158 * return the data. There is also the chance that an entry is removed by eviction, 159 * or is never inserted to the cache, before the get returns it. 160 * 161 * <p/>Even if this is true, the data may be expired. Use hasFreshData() to 162 * make sure to get not expired data. 163 */ 164 public final boolean isDataValidState() { 165 return nextRefreshTime >= FETCHED_STATE || nextRefreshTime < 0; 166 } 167 168 /** 169 * Starts long operation on entry. Pins the entry in the cache. 170 */ 171 public void startFetch() { 172 if (isVirgin()) { 173 nextRefreshTime = FETCH_IN_PROGRESS_VIRGIN; 174 } else { 175 nextRefreshTime = FETCH_IN_PROGRESS_NON_VALID; 176 } 177 } 178 179 public void finishFetch(long _nextRefreshTime) { 180 synchronized (Entry.this) { 181 nextRefreshTime = _nextRefreshTime; 182 notifyAll(); 183 } 184 } 185 186 /** 187 * If fetch is not stopped, abort and make entry invalid. 188 * This is a safety measure, since during entry processing an 189 * exceptions may happen. This can happen regularly e.g. if storage 190 * is set to read only and a cache put is made. 191 */ 192 public void ensureFetchAbort(boolean _finished) { 193 if (_finished) { 194 return; 195 } 196 if (isFetchInProgress()) { 197 synchronized (Entry.this) { 198 if (isFetchInProgress()) { 199 nextRefreshTime = FETCH_ABORT; 200 notifyAll(); 201 } 202 } 203 } 204 } 205 206 /** 207 * Entry is not allowed to be evicted 208 */ 209 public boolean isPinned() { 210 return isFetchInProgress(); 211 } 212 213 public boolean isFetchInProgress() { 214 return 215 nextRefreshTime == REFRESH_STATE || 216 nextRefreshTime == LOADED_NON_VALID_AND_FETCH || 217 nextRefreshTime == FETCH_IN_PROGRESS_VIRGIN || 218 nextRefreshTime == LOADED_NON_VALID_AND_PUT || 219 nextRefreshTime == FETCH_IN_PROGRESS_NON_VALID || 220 nextRefreshTime == FETCH_IN_PROGRESS_VALID; 221 } 222 223 public void waitForFetch() { 224 if (!isFetchInProgress()) { 225 return; 226 } 227 try { 228 do { 229 wait(); 230 } while (isFetchInProgress()); 231 } catch (InterruptedException e) { 232 throw new CacheInternalError(); 233 } 234 } 235 236 /** 237 * Returns true if the entry has a valid value and is fresh / not expired. 238 */ 239 public final boolean hasFreshData() { 240 if (nextRefreshTime >= FETCHED_STATE) { 241 return true; 242 } 243 if (needsTimeCheck()) { 244 long now = System.currentTimeMillis(); 245 return now < -nextRefreshTime; 246 } 247 return false; 248 } 249 250 /** 251 * Same as {@link #hasFreshData}, optimization if current time is known. 252 */ 253 public final boolean hasFreshData(long now) { 254 if (nextRefreshTime >= FETCHED_STATE) { 255 return true; 256 } 257 if (needsTimeCheck()) { 258 return now < -nextRefreshTime; 259 } 260 return false; 261 } 262 263 public final boolean hasFreshData(long now, long _nextRefreshTime) { 264 if (_nextRefreshTime >= FETCHED_STATE) { 265 return true; 266 } 267 if (_nextRefreshTime < 0) { 268 return now < -_nextRefreshTime; 269 } 270 return false; 271 } 272 273 public boolean isLoadedNonValid() { 274 return nextRefreshTime == LOADED_NON_VALID; 275 } 276 277 public void setLoadedNonValidAndFetch() { 278 nextRefreshTime = LOADED_NON_VALID_AND_FETCH; 279 } 280 281 public boolean isLoadedNonValidAndFetch() { 282 return nextRefreshTime == LOADED_NON_VALID_AND_FETCH; 283 } 284 285 /** Entry is kept in the cache but has expired */ 286 public void setExpiredState() { 287 nextRefreshTime = EXPIRED_STATE; 288 } 289 290 /** 291 * The entry expired, but still in the cache. This may happen if 292 * {@link BaseCache#hasKeepAfterExpired()} is true. 293 */ 294 public boolean isExpiredState() { 295 return nextRefreshTime == EXPIRED_STATE; 296 } 297 298 public void setRemovedState() { 299 nextRefreshTime = REMOVED_STATE; 300 } 301 302 public boolean isRemovedState() { 303 return nextRefreshTime == REMOVED_STATE; 304 } 305 306 public void setGettingRefresh() { 307 nextRefreshTime = REFRESH_STATE; 308 } 309 310 public boolean isGettingRefresh() { 311 return nextRefreshTime == REFRESH_STATE; 312 } 313 314 public boolean isBeeingReput() { 315 return nextRefreshTime == REPUT_STATE; 316 } 317 318 public boolean needsTimeCheck() { 319 return nextRefreshTime < 0; 320 } 321 322 public boolean isStale() { 323 return STALE_MARKER_KEY == key; 324 } 325 326 public void setStale() { 327 key = (K) STALE_MARKER_KEY; 328 } 329 330 public boolean hasException() { 331 return value instanceof ExceptionWrapper; 332 } 333 334 public Throwable getException() { 335 if (value instanceof ExceptionWrapper) { 336 return ((ExceptionWrapper) value).getException(); 337 } 338 return null; 339 } 340 341 public void setException(Throwable exception) { 342 value = (T) new ExceptionWrapper(exception); 343 } 344 345 public T getValue() { 346 if (value instanceof ExceptionWrapper) { return null; } 347 return value; 348 } 349 350 351 @Override 352 public void setValue(T v) { 353 value = v; 354 } 355 356 @Override 357 public K getKey() { 358 return key; 359 } 360 361 @Override 362 public long getLastModification() { 363 return fetchedTime >> 1; 364 } 365 366 /** 367 * Expiry time or 0. 368 */ 369 public long getValueExpiryTime() { 370 if (nextRefreshTime < 0) { 371 return -nextRefreshTime; 372 } else if (nextRefreshTime > EXPIRY_TIME_MIN) { 373 return nextRefreshTime; 374 } 375 return 0; 376 } 377 378 /** 379 * Used for the storage interface. 380 * 381 * @see org.cache2k.storage.StorageEntry 382 */ 383 @Override 384 public Object getValueOrException() { 385 return value; 386 } 387 388 /** 389 * Used for the storage interface. 390 * 391 * @see org.cache2k.storage.StorageEntry 392 */ 393 @Override 394 public long getCreatedOrUpdated() { 395 return getLastModification(); 396 } 397 398 /** 399 * Used for the storage interface. 400 * 401 * @see org.cache2k.storage.StorageEntry 402 * @deprectated Always returns 0, only to fulfill the {@link org.cache2k.storage.StorageEntry} interface 403 */ 404 @Override 405 public long getEntryExpiryTime() { 406 return 0; 407 } 408 409 @Override 410 public String toString() { 411 return "Entry{" + 412 "createdOrUpdate=" + getCreatedOrUpdated() + 413 ", nextRefreshTime=" + nextRefreshTime + 414 ", valueExpiryTime=" + getValueExpiryTime() + 415 ", entryExpiryTime=" + getEntryExpiryTime() + 416 ", key=" + key + 417 ", mHC=" + hashCode + 418 ", value=" + value + 419 ", dirty=" + isDirty() + 420 '}'; 421 } 422 423 /** 424 * Cache entries always have the object identity as equals method. 425 */ 426 @Override 427 public final boolean equals(Object obj) { 428 return this == obj; 429 } 430 431 /* check entry states */ 432 static { 433 Entry e = new Entry(); 434 e.nextRefreshTime = FETCHED_STATE; 435 e.setGettingRefresh(); 436 e = new Entry(); 437 e.setLoadedNonValidAndFetch(); 438 e.setExpiredState(); 439 } 440 441 static class InitialValueInEntryNeverReturned extends Object { } 442 443 static class StaleMarker { 444 @Override 445 public boolean equals(Object o) { return false; } 446 } 447 448}