/*
* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/
/*
File: AbstractConcurrentReadCache
Written by Doug Lea. Adapted from JDK1.2 HashMap.java and Hashtable.java
which carries the following copyright:
* Copyright 1997 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
This class is a modified version of ConcurrentReaderHashMap, which was written
by Doug Lea (http://gee.cs.oswego.edu/dl/). The modifications where done
by Pyxis Technologies. This is a base class for the OSCache module of the
openSymphony project (www.opensymphony.com).
History:
Date Who What
28oct1999 dl Created
14dec1999 dl jmm snapshot
19apr2000 dl use barrierLock
12jan2001 dl public release
Oct2001 abergevin@pyxis-tech.com
Integrated persistence and outer algorithm support
*/
package com.opensymphony.oscache.base.algorithm;
/** OpenSymphony BEGIN */
import com.opensymphony.oscache.base.CacheEntry;
import com.opensymphony.oscache.base.persistence.CachePersistenceException;
import com.opensymphony.oscache.base.persistence.PersistenceListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
/**
* A version of Hashtable that supports mostly-concurrent reading, but exclusive writing.
* Because reads are not limited to periods
* without writes, a concurrent reader policy is weaker than a classic
* reader/writer policy, but is generally faster and allows more
* concurrency. This class is a good choice especially for tables that
* are mainly created by one thread during the start-up phase of a
* program, and from then on, are mainly read (with perhaps occasional
* additions or removals) in many threads. If you also need concurrency
* among writes, consider instead using ConcurrentHashMap.
* <p>
*
* Successful retrievals using get(key) and containsKey(key) usually
* run without locking. Unsuccessful ones (i.e., when the key is not
* present) do involve brief synchronization (locking). Also, the
* size and isEmpty methods are always synchronized.
*
* <p> Because retrieval operations can ordinarily overlap with
* writing operations (i.e., put, remove, and their derivatives),
* retrievals can only be guaranteed to return the results of the most
* recently <em>completed</em> operations holding upon their
* onset. Retrieval operations may or may not return results
* reflecting in-progress writing operations. However, the retrieval
* operations do always return consistent results -- either those
* holding before any single modification or after it, but never a
* nonsense result. For aggregate operations such as putAll and
* clear, concurrent reads may reflect insertion or removal of only
* some entries. In those rare contexts in which you use a hash table
* to synchronize operations across threads (for example, to prevent
* reads until after clears), you should either encase operations
* in synchronized blocks, or instead use java.util.Hashtable.
*
* <p>
*
* This class also supports optional guaranteed
* exclusive reads, simply by surrounding a call within a synchronized
* block, as in <br>
* <code>AbstractConcurrentReadCache t; ... Object v; <br>
* synchronized(t) { v = t.get(k); } </code> <br>
*
* But this is not usually necessary in practice. For
* example, it is generally inefficient to write:
*
* <pre>
* AbstractConcurrentReadCache t; ... // Inefficient version
* Object key; ...
* Object value; ...
* synchronized(t) {
* if (!t.containsKey(key))
* t.put(key, value);
* // other code if not previously present
* }
* else {
* // other code if it was previously present
* }
* }
*</pre>
* Instead, just take advantage of the fact that put returns
* null if the key was not previously present:
* <pre>
* AbstractConcurrentReadCache t; ... // Use this instead
* Object key; ...
* Object value; ...
* Object oldValue = t.put(key, value);
* if (oldValue == null) {
* // other code if not previously present
* }
* else {
* // other code if it was previously present
* }
*</pre>
* <p>
*
* Iterators and Enumerations (i.e., those returned by
* keySet().iterator(), entrySet().iterator(), values().iterator(),
* keys(), and elements()) return elements reflecting the state of the
* hash table at some point at or since the creation of the
* iterator/enumeration. They will return at most one instance of
* each element (via next()/nextElement()), but might or might not
* reflect puts and removes that have been processed since they were
* created. They do <em>not</em> throw ConcurrentModificationException.
* However, these iterators are designed to be used by only one
* thread at a time. Sharing an iterator across multiple threads may
* lead to unpredictable results if the table is being concurrently
* modified. Again, you can ensure interference-free iteration by
* enclosing the iteration in a synchronized block. <p>
*
* This class may be used as a direct replacement for any use of
* java.util.Hashtable that does not depend on readers being blocked
* during updates. Like Hashtable but unlike java.util.HashMap,
* this class does NOT allow <tt>null</tt> to be used as a key or
* value. This class is also typically faster than ConcurrentHashMap
* when there is usually only one thread updating the table, but
* possibly many retrieving values from it.
* <p>
*
* Implementation note: A slightly faster implementation of
* this class will be possible once planned Java Memory Model
* revisions are in place.
*
* <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
**/
public abstract class AbstractConcurrentReadCache extends AbstractMap implements Map, Cloneable, Serializable {
/**
* The default initial number of table slots for this table (32).
* Used when not otherwise specified in constructor.
**/
public static int DEFAULT_INITIAL_CAPACITY = 32;
/**
* The minimum capacity.
* Used if a lower value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two.
*/
private static final int MINIMUM_CAPACITY = 4;
/**
* The maximum capacity.
* Used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default load factor for this table.
* Used when not otherwise specified in constructor, the default is 0.75f.
**/
public static final float DEFAULT_LOAD_FACTOR = 0.75f;
//OpenSymphony BEGIN (pretty long!)
protected static final String NULL = "_nul!~";
protected static Log log = LogFactory.getLog(AbstractConcurrentReadCache.class);
/*
The basic strategy is an optimistic-style scheme based on
the guarantee that the hash table and its lists are always
kept in a consistent enough state to be read without locking:
* Read operations first proceed without loc