openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From p..@apache.org
Subject svn commit: r417856 [19/22] - in /incubator/openjpa/trunk/openjpa-lib: java/ main/ main/java/ main/java/org/apache/openjpa/lib/ant/ main/java/org/apache/openjpa/lib/conf/ main/java/org/apache/openjpa/lib/jdbc/ main/java/org/apache/openjpa/lib/log/ main...
Date Wed, 28 Jun 2006 19:34:40 GMT
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,1161 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import org.apache.openjpa.lib.util.ReferenceMap;
+import org.apache.openjpa.lib.util.SizedMap;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+
+
+/**
+ * This class implements a HashMap which has limited synchronization
+ * and reference keys or values (but not both). In particular mutators are
+ * generally synchronized while accessors are generally not. Additionally the
+ * Iterators returned by this class are not "fail-fast", but instead try to
+ * continue to iterate over the data structure after changes have been
+ * made. Finally purging of the reference queue is only done inside
+ * mutators.
+ *
+ * Null keys are not supported if keys use references.  Null values are not
+ * supported if values use references.
+ *
+ * This class is based heavily on the WeakHashMap class in the Java
+ * collections package.
+ */
+public class ConcurrentReferenceHashMap extends AbstractMap
+    implements ConcurrentMap, ReferenceMap, SizedMap, Cloneable {
+    /**
+     * Cache of random numbers used in "random" methods, since generating them
+     * is expensive.  We hope each map changes enough between cycling through
+      * this list that the overall effect is random enough.
+     */
+    static final double[] RANDOMS = new double[1000];
+
+    static {
+        Random random = new Random();
+
+        for (int i = 0; i < RANDOMS.length; i++)
+            RANDOMS[i] = random.nextDouble();
+    }
+
+    // Types of Enumerations/Iterations
+    private static final int KEYS = 0;
+    private static final int VALUES = 1;
+    private static final int ENTRIES = 2;
+
+    /**
+     * The hash table data.
+     */
+    private transient Entry[] table;
+
+    /**
+     * The total number of entries in the hash table.
+     */
+    private transient int count;
+
+    /**
+     * Rehashes the table when count exceeds this threshold.
+     */
+    private int threshold;
+
+    /**
+     * The load factor for the HashMap.
+     */
+    private float loadFactor;
+
+    /**
+     * The key reference type.
+     */
+    private int keyType;
+
+    /**
+     * The value reference type.
+     */
+    private int valueType;
+
+    /**
+     * Reference queue for cleared Entries
+     */
+    private final ReferenceQueue queue = new ReferenceQueue();
+
+    /**
+     * Spread "random" removes and iteration.
+     */
+    private int randomEntry = 0;
+
+    /**
+     * Maximum entries.
+     */
+    private int maxSize = Integer.MAX_VALUE;
+
+    // Views
+    private transient Set keySet = null;
+    private transient Set entrySet = null;
+    private transient Collection values = null;
+
+    /**
+     * Constructs a new, empty HashMap with the specified initial
+     * capacity and the specified load factor.
+     *
+         * @param keyType the reference type of map keys
+         * @param valueType the reference type of map values
+     * @param initialCapacity the initial capacity of the HashMap.
+     * @param loadFactor a number between 0.0 and 1.0.
+     * @exception IllegalArgumentException if neither keys nor values use hard
+         * references, if the initial capacity is less than or equal to zero, or if
+         * the load factor is less than or equal to zero
+     */
+    public ConcurrentReferenceHashMap(int keyType, int valueType,
+        int initialCapacity, float loadFactor) {
+        if (initialCapacity < 0) {
+            throw new IllegalArgumentException("Illegal Initial Capacity: " +
+                initialCapacity);
+        }
+
+        if ((loadFactor > 1) || (loadFactor <= 0)) {
+            throw new IllegalArgumentException("Illegal Load factor: " +
+                loadFactor);
+        }
+
+        if ((keyType != HARD) && (valueType != HARD)) {
+            throw new IllegalArgumentException("Either keys or values must " +
+                "use hard references.");
+        }
+
+        this.keyType = keyType;
+        this.valueType = valueType;
+        this.loadFactor = loadFactor;
+        table = new Entry[initialCapacity];
+        threshold = (int) (initialCapacity * loadFactor);
+    }
+
+    /**
+     * Constructs a new, empty HashMap with the specified initial capacity
+     * and default load factor.
+     *
+         * @param keyType the reference type of map keys
+         * @param valueType the reference type of map values
+     * @param initialCapacity the initial capacity of the HashMap.
+     */
+    public ConcurrentReferenceHashMap(int keyType, int valueType,
+        int initialCapacity) {
+        this(keyType, valueType, initialCapacity, 0.75f);
+    }
+
+    /**
+     * Constructs a new, empty HashMap with a default capacity and load
+     * factor.
+         *
+         * @param keyType the reference type of map keys
+         * @param valueType the reference type of map values
+     */
+    public ConcurrentReferenceHashMap(int keyType, int valueType) {
+        this(keyType, valueType, 11, 0.75f);
+    }
+
+    /**
+     * Constructs a new HashMap with the same mappings as the given
+     * Map. The HashMap is created with a capacity of thrice the number
+     * of entries in the given Map or 11 (whichever is greater), and a
+     * default load factor.
+         *
+         * @param keyType the reference type of map keys
+         * @param valueType the reference type of map values
+     */
+    public ConcurrentReferenceHashMap(int keyType, int valueType, Map t) {
+        this(keyType, valueType, Math.max(3 * t.size(), 11), 0.75f);
+        putAll(t);
+    }
+
+    private static boolean eq(Object x, Object y) {
+        return (x == y) || ((x != null) && x.equals(y));
+    }
+
+    public int getMaxSize() {
+        return maxSize;
+    }
+
+    public void setMaxSize(int maxSize) {
+        this.maxSize = (maxSize < 0) ? Integer.MAX_VALUE : maxSize;
+
+        if (this.maxSize != Integer.MAX_VALUE) {
+            removeOverflow(this.maxSize);
+        }
+    }
+
+    public boolean isFull() {
+        return (maxSize != Integer.MAX_VALUE) && (size() >= maxSize);
+    }
+
+    public void overflowRemoved(Object key, Object value) {
+    }
+
+    /**
+     * Returns the number of key-value mappings in this Map.    This
+     * result is a snapshot, and may not reflect unprocessed entries
+     * that will be removed before next attempted access because they
+     * are no longer referenced.
+     */
+    public int size() {
+        return count;
+    }
+
+    /**
+     * Returns true if this Map contains no key-value mappings.    This
+     * result is a snapshot, and may not reflect unprocessed entries
+     * that will be removed before next attempted access because they
+     * are no longer referenced.
+     */
+    public boolean isEmpty() {
+        return count == 0;
+    }
+
+    /**
+     * Returns true if this HashMap maps one or more keys to the specified
+     * value.
+     *
+     * @param value value whose presence in this Map is to be tested.
+     */
+    public boolean containsValue(Object value) {
+        Entry[] tab = table;
+
+        if (value == null) {
+            if (valueType != HARD) {
+                return false;
+            }
+
+            for (int i = tab.length; i-- > 0;)
+                for (Entry e = tab[i]; e != null; e = e.getNext())
+                    if (e.getValue() == null) {
+                        return true;
+                    }
+        } else {
+            for (int i = tab.length; i-- > 0;)
+                for (Entry e = tab[i]; e != null; e = e.getNext())
+                    if (eq(value, e.getValue())) {
+                        return true;
+                    }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns true if this HashMap contains a mapping for the specified key.
+     *
+     * @param key key whose presence in this Map is to be tested.
+     */
+    public boolean containsKey(Object key) {
+        if ((key == null) && (keyType != HARD)) {
+            return false;
+        }
+
+        Entry[] tab = table;
+        int hash = (key == null) ? 0 : key.hashCode();
+        int index = (hash & 0x7FFFFFFF) % tab.length;
+
+        for (Entry e = tab[index]; e != null; e = e.getNext())
+            if ((e.getHash() == hash) && eq(key, e.getKey())) {
+                return true;
+            }
+
+        return false;
+    }
+
+    /**
+     * Returns the value to which this HashMap maps the specified key.
+     * Returns null if the HashMap contains no mapping for this key.
+     *
+     * @param key key whose associated value is to be returned.
+     */
+    public Object get(Object key) {
+        if ((key == null) && (keyType != HARD)) {
+            return null;
+        }
+
+        Entry[] tab = table;
+        int hash = (key == null) ? 0 : key.hashCode();
+        int index = (hash & 0x7FFFFFFF) % tab.length;
+
+        for (Entry e = tab[index]; e != null; e = e.getNext())
+            if ((e.getHash() == hash) && eq(key, e.getKey())) {
+                return e.getValue();
+            }
+
+        return null;
+    }
+
+    /**
+     * Rehashes the contents of the HashMap into a HashMap with a
+     * larger capacity. This method is called automatically when the
+     * number of keys in the HashMap exceeds this HashMap's capacity
+     * and load factor.
+     */
+    private void rehash() {
+        int oldCapacity = table.length;
+        Entry[] oldMap = table;
+
+        int newCapacity = (oldCapacity * 2) + 1;
+        Entry[] newMap = new Entry[newCapacity];
+
+        for (int i = oldCapacity; i-- > 0;) {
+            for (Entry old = oldMap[i]; old != null;) {
+                if (((keyType != HARD) && (old.getKey() == null)) ||
+                        ((valueType != HARD) && (old.getValue() == null))) {
+                    Entry e = old;
+                    old = old.getNext();
+                    e.setNext(null);
+                    count--;
+                } else {
+                    Entry e = (Entry) old.clone(queue);
+                    old = old.getNext();
+
+                    int index = (e.getHash() & 0x7FFFFFFF) % newCapacity;
+                    e.setNext(newMap[index]);
+                    newMap[index] = e;
+                }
+            }
+        }
+
+        threshold = (int) (newCapacity * loadFactor);
+        table = newMap;
+    }
+
+    /**
+     * Associates the specified value with the specified key in this HashMap.
+     * If the HashMap previously contained a mapping for this key, the old
+     * value is replaced.
+     *
+     * @param key key with which the specified value is to be associated.
+     * @param value value to be associated with the specified key.
+     * @return previous value associated with specified key, or null if there
+     * was no mapping for key.  A null return can also indicate that
+     * the HashMap previously associated null with the specified key.
+     */
+    public Object put(Object key, Object value) {
+        if (((key == null) && (keyType != HARD)) ||
+                ((value == null) && (valueType != HARD))) {
+            throw new IllegalArgumentException("Null references not supported");
+        }
+
+        int hash = (key == null) ? 0 : key.hashCode();
+
+        synchronized (this) {
+            expungeStaleEntries();
+
+            Entry[] tab = table;
+            int index = 0;
+
+            index = (hash & 0x7FFFFFFF) % tab.length;
+
+            for (Entry e = tab[index], prev = null; e != null;
+                    prev = e, e = e.getNext()) {
+                if ((e.getHash() == hash) && eq(key, e.getKey())) {
+                    Object old = e.getValue();
+
+                    if (valueType == HARD) {
+                        e.setValue(value);
+                    } else {
+                        e = newEntry(hash, e.getKey(), value, e.getNext());
+
+                        if (prev == null) {
+                            tab[index] = e;
+                        } else {
+                            prev.setNext(e);
+                        }
+                    }
+
+                    return old;
+                }
+            }
+
+            if (count >= threshold) {
+                // Rehash the table if the threshold is exceeded
+                rehash();
+
+                tab = table;
+                index = (hash & 0x7FFFFFFF) % tab.length;
+            }
+
+            if (maxSize != Integer.MAX_VALUE) {
+                removeOverflow(maxSize - 1);
+            }
+
+            tab[index] = newEntry(hash, key, value, tab[index]);
+            count++;
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a new entry.
+     */
+    private Entry newEntry(int hash, Object key, Object value, Entry next) {
+        int refType = (keyType != HARD) ? keyType : valueType;
+
+        switch (refType) {
+        case WEAK:
+            return new WeakEntry(hash, key, value, refType == keyType, next,
+                queue);
+
+        case SOFT:
+            return new SoftEntry(hash, key, value, refType == keyType, next,
+                queue);
+
+        default:
+            return new HardEntry(hash, key, value, next);
+        }
+    }
+
+    /**
+     * Remove any entries equal to or over the max size.
+      */
+    private void removeOverflow(int maxSize) {
+        while (count > maxSize) {
+            Map.Entry entry = removeRandom();
+
+            if (entry == null) {
+                break;
+            }
+
+            overflowRemoved(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Removes the mapping for this key from this HashMap if present.
+     *
+     * @param key key whose mapping is to be removed from the Map.
+     * @return previous value associated with specified key, or null if there
+     * was no mapping for key. A null return can also indicate that
+     * the HashMap previously associated null with the specified key.
+     */
+    public Object remove(Object key) {
+        if ((key == null) && (keyType != HARD)) {
+            return null;
+        }
+
+        int hash = (key == null) ? 0 : key.hashCode();
+
+        synchronized (this) {
+            expungeStaleEntries();
+
+            Entry[] tab = table;
+
+            int index = (hash & 0x7FFFFFFF) % tab.length;
+
+            for (Entry e = tab[index], prev = null; e != null;
+                    prev = e, e = e.getNext()) {
+                if ((e.getHash() == hash) && eq(key, e.getKey())) {
+                    if (prev != null) {
+                        prev.setNext(e.getNext());
+                    }
+                    // otherwise put the bucket after us
+                    else {
+                        tab[index] = e.getNext();
+                    }
+
+                    count--;
+
+                    return e.getValue();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public void removeExpired() {
+        synchronized (this) {
+            expungeStaleEntries();
+        }
+    }
+
+    public void keyExpired(Object value) {
+    }
+
+    public void valueExpired(Object key) {
+    }
+
+    /**
+     * Return an arbitrary entry index.
+     */
+    private int randomEntryIndex() {
+        if (randomEntry == RANDOMS.length) {
+            randomEntry = 0;
+        }
+
+        return (int) (RANDOMS[randomEntry++] * table.length);
+    }
+
+    public Map.Entry removeRandom() {
+        synchronized (this) {
+            expungeStaleEntries();
+
+            if (count == 0) {
+                return null;
+            }
+
+            int random = randomEntryIndex();
+            int index = findEntry(random, (random % 2) == 0, false);
+
+            if (index == -1) {
+                return null;
+            }
+
+            Entry rem = table[index];
+            table[index] = rem.getNext();
+            count--;
+
+            return rem;
+        }
+    }
+
+    /**
+     * Find the index of the entry nearest the given index, starting in the
+     * given direction.
+     */
+    private int findEntry(int start, boolean forward, boolean searchedOther) {
+        if (forward) {
+            for (int i = start; i < table.length; i++)
+                if (table[i] != null) {
+                    return i;
+                }
+
+            return (searchedOther || (start == 0)) ? (-1)
+                                                   : findEntry(start - 1,
+                false, true);
+        } else {
+            for (int i = start; i >= 0; i--)
+                if (table[i] != null) {
+                    return i;
+                }
+
+            return (searchedOther || (start == (table.length - 1))) ? (-1)
+                                                                    : findEntry(start +
+                1, true, true);
+        }
+    }
+
+    public Iterator randomEntryIterator() {
+        // pass index so calculated before iterator refs table, in case table
+        // gets replace with a larger one
+        return new HashIterator(ENTRIES, randomEntryIndex());
+    }
+
+    /**
+     * Copies all of the mappings from the specified Map to this HashMap
+     * These mappings will replace any mappings that this HashMap had for any
+     * of the keys currently in the specified Map.
+     *
+     * @param t Mappings to be stored in this Map.
+     */
+    public void putAll(Map t) {
+        Iterator i = t.entrySet().iterator();
+
+        while (i.hasNext()) {
+            Map.Entry e = (Map.Entry) i.next();
+            put(e.getKey(), e.getValue());
+        }
+    }
+
+    /**
+     * Removes all mappings from this HashMap.
+     */
+    public synchronized void clear() {
+        // clear out ref queue. We don't need to expunge entries
+        // since table is getting cleared.
+        while (queue.poll() != null)
+            ;
+
+        table = new Entry[table.length];
+        count = 0;
+
+        // Allocation of array may have caused GC, which may have caused
+        // additional entries to go stale.    Removing these entries from
+        // the reference queue will make them eligible for reclamation.
+        while (queue.poll() != null)
+            ;
+    }
+
+    /**
+     * Returns a shallow copy of this HashMap. The keys and values
+     * themselves are not cloned.
+     */
+    public synchronized Object clone() {
+        try {
+            expungeStaleEntries();
+
+            ConcurrentReferenceHashMap t = (ConcurrentReferenceHashMap) super.clone();
+            t.table = new Entry[table.length];
+
+            for (int i = table.length; i-- > 0;) {
+                Entry e = table[i];
+
+                if (e != null) {
+                    t.table[i] = (Entry) e.clone(t.queue);
+                    e = e.getNext();
+
+                    for (Entry k = t.table[i]; e != null; e = e.getNext()) {
+                        k.setNext((Entry) e.clone(t.queue));
+                        k = k.getNext();
+                    }
+                }
+            }
+
+            t.keySet = null;
+            t.entrySet = null;
+            t.values = null;
+
+            return t;
+        } catch (CloneNotSupportedException e) {
+            // this shouldn't happen, since we are Cloneable
+            throw new InternalError();
+        }
+    }
+
+    /**
+     * Returns a Set view of the keys contained in this HashMap.    The Set is
+     * backed by the HashMap, so changes to the HashMap are reflected in the
+     * Set, and vice-versa. The Set supports element removal, which removes
+     * the corresponding mapping from the HashMap, via the Iterator.remove,
+     * Set.remove, removeAll retainAll, and clear operations. It does not
+     * support the add or addAll operations.
+     */
+    public Set keySet() {
+        if (keySet == null) {
+            keySet = new java.util.AbstractSet() {
+                        public Iterator iterator() {
+                            return new HashIterator(KEYS, table.length - 1);
+                        }
+
+                        public int size() {
+                            return count;
+                        }
+
+                        public boolean contains(Object o) {
+                            return containsKey(o);
+                        }
+
+                        public boolean remove(Object o) {
+                            return ConcurrentReferenceHashMap.this.remove(o) != null;
+                        }
+
+                        public void clear() {
+                            ConcurrentReferenceHashMap.this.clear();
+                        }
+                    };
+        }
+
+        return keySet;
+    }
+
+    /**
+     * Returns a Collection view of the values contained in this HashMap.
+     * The Collection is backed by the HashMap, so changes to the HashMap are
+     * reflected in the Collection, and vice-versa. The Collection supports
+     * element removal, which removes the corresponding mapping from the
+     * HashMap, via the Iterator.remove, Collection.remove, removeAll,
+     * retainAll and clear operations. It does not support the add or addAll
+     * operations.
+     */
+    public Collection values() {
+        if (values == null) {
+            values = new java.util.AbstractCollection() {
+                        public Iterator iterator() {
+                            return new HashIterator(VALUES, table.length - 1);
+                        }
+
+                        public int size() {
+                            return count;
+                        }
+
+                        public boolean contains(Object o) {
+                            return containsValue(o);
+                        }
+
+                        public void clear() {
+                            ConcurrentReferenceHashMap.this.clear();
+                        }
+                    };
+        }
+
+        return values;
+    }
+
+    /**
+     * Returns a Collection view of the mappings contained in this HashMap.
+     * Each element in the returned collection is a Map.Entry.    The Collection
+     * is backed by the HashMap, so changes to the HashMap are reflected in the
+     * Collection, and vice-versa.    The Collection supports element removal,
+     * which removes the corresponding mapping from the HashMap, via the
+     * Iterator.remove, Collection.remove, removeAll, retainAll and clear
+     * operations.    It does not support the add or addAll operations.
+     *
+     * @see     Map.Entry
+     */
+    public Set entrySet() {
+        if (entrySet == null) {
+            entrySet = new java.util.AbstractSet() {
+                        public Iterator iterator() {
+                            return new HashIterator(ENTRIES, table.length - 1);
+                        }
+
+                        public boolean contains(Object o) {
+                            if (!(o instanceof Map.Entry)) {
+                                return false;
+                            }
+
+                            Map.Entry entry = (Map.Entry) o;
+                            Object key = entry.getKey();
+                            Entry[] tab = table;
+                            int hash = ((key == null) ? 0 : key.hashCode());
+                            int index = (hash & 0x7FFFFFFF) % tab.length;
+
+                            for (Entry e = tab[index]; e != null;
+                                    e = e.getNext())
+                                if ((e.getHash() == hash) && eq(e, entry)) {
+                                    return true;
+                                }
+
+                            return false;
+                        }
+
+                        public boolean remove(Object o) {
+                            if (!(o instanceof Map.Entry)) {
+                                return false;
+                            }
+
+                            Map.Entry entry = (Map.Entry) o;
+                            Object key = entry.getKey();
+
+                            synchronized (ConcurrentReferenceHashMap.this) {
+                                Entry[] tab = table;
+                                int hash = ((key == null) ? 0 : key.hashCode());
+                                int index = (hash & 0x7FFFFFFF) % tab.length;
+
+                                for (Entry e = tab[index], prev = null;
+                                        e != null; prev = e, e = e.getNext()) {
+                                    if ((e.getHash() == hash) && eq(e, entry)) {
+                                        if (prev != null) {
+                                            prev.setNext(e.getNext());
+                                        } else {
+                                            tab[index] = e.getNext();
+                                        }
+
+                                        count--;
+
+                                        return true;
+                                    }
+                                }
+
+                                return false;
+                            }
+                        }
+
+                        public int size() {
+                            return count;
+                        }
+
+                        public void clear() {
+                            ConcurrentReferenceHashMap.this.clear();
+                        }
+                    };
+        }
+
+        return entrySet;
+    }
+
+    /**
+     * Expunge stale entries from the table.
+     */
+    private void expungeStaleEntries() {
+        Object r;
+
+        while ((r = queue.poll()) != null) {
+            Entry entry = (Entry) r;
+            int hash = entry.getHash();
+            Entry[] tab = table;
+            int index = (hash & 0x7FFFFFFF) % tab.length;
+
+            for (Entry e = tab[index], prev = null; e != null;
+                    prev = e, e = e.getNext()) {
+                if (e == entry) {
+                    if (prev != null) {
+                        prev.setNext(e.getNext());
+                    }
+                    // otherwise put the bucket after us
+                    else {
+                        tab[index] = e.getNext();
+                    }
+
+                    count--;
+
+                    if (keyType == HARD) {
+                        valueExpired(e.getKey());
+                    } else {
+                        keyExpired(e.getValue());
+                    }
+                }
+            }
+        }
+    }
+
+    int capacity() {
+        return table.length;
+    }
+
+    float loadFactor() {
+        return loadFactor;
+    }
+
+    /**
+     * HashMap collision list entry.
+     */
+    private static interface Entry extends Map.Entry {
+        public int getHash();
+
+        public Entry getNext();
+
+        public void setNext(Entry next);
+
+        public Object clone(ReferenceQueue queue);
+    }
+
+    /**
+     * Hard entry.
+     */
+    private static class HardEntry implements Entry {
+        private int hash;
+        private Object key;
+        private Object value;
+        private Entry next;
+
+        HardEntry(int hash, Object key, Object value, Entry next) {
+            this.hash = hash;
+            this.key = key;
+            this.value = value;
+            this.next = next;
+        }
+
+        public int getHash() {
+            return hash;
+        }
+
+        public Entry getNext() {
+            return next;
+        }
+
+        public void setNext(Entry next) {
+            this.next = next;
+        }
+
+        public Object clone(ReferenceQueue queue) {
+            // It is the callers responsibility to set the next field
+            // correctly.
+            return new HardEntry(hash, key, value, null);
+        }
+
+        // Map.Entry Ops 
+        public Object getKey() {
+            return key;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object setValue(Object value) {
+            Object oldValue = this.value;
+            this.value = value;
+
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+
+            Map.Entry e = (Map.Entry) o;
+
+            Object k1 = key;
+            Object k2 = e.getKey();
+
+            return ((k1 == null) ? (k2 == null) : eq(k1, k2)) &&
+            ((value == null) ? (e.getValue() == null) : eq(value, e.getValue()));
+        }
+
+        public int hashCode() {
+            return hash ^ ((value == null) ? 0 : value.hashCode());
+        }
+
+        public String toString() {
+            return key + "=" + value.toString();
+        }
+    }
+
+    /**
+     * Weak entry.
+     */
+    private static class WeakEntry extends WeakReference implements Entry {
+        private int hash;
+        private Object hard;
+        private boolean keyRef;
+        private Entry next;
+
+        WeakEntry(int hash, Object key, Object value, boolean keyRef,
+            Entry next, ReferenceQueue queue) {
+            super((keyRef) ? key : value, queue);
+            this.hash = hash;
+            this.hard = (keyRef) ? value : key;
+            this.keyRef = keyRef;
+            this.next = next;
+        }
+
+        public int getHash() {
+            return hash;
+        }
+
+        public Entry getNext() {
+            return next;
+        }
+
+        public void setNext(Entry next) {
+            this.next = next;
+        }
+
+        public Object clone(ReferenceQueue queue) {
+            // It is the callers responsibility to set the next field
+            // correctly.
+            return new WeakEntry(hash, getKey(), getValue(), keyRef, null, queue);
+        }
+
+        // Map.Entry Ops 
+        public Object getKey() {
+            return (keyRef) ? super.get() : hard;
+        }
+
+        public Object getValue() {
+            return (keyRef) ? hard : super.get();
+        }
+
+        public Object setValue(Object value) {
+            if (!keyRef) {
+                throw new Error("Attempt to reset reference value.");
+            }
+
+            Object oldValue = hard;
+            hard = value;
+
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+
+            Map.Entry e = (Map.Entry) o;
+
+            return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue());
+        }
+
+        public int hashCode() {
+            Object val = getValue();
+
+            return hash ^ ((val == null) ? 0 : val.hashCode());
+        }
+
+        public String toString() {
+            return getKey() + "=" + getValue();
+        }
+    }
+
+    /**
+     * Soft entry.
+     */
+    private static class SoftEntry extends SoftReference implements Entry {
+        private int hash;
+        private Object hard;
+        private boolean keyRef;
+        private Entry next;
+
+        SoftEntry(int hash, Object key, Object value, boolean keyRef,
+            Entry next, ReferenceQueue queue) {
+            super((keyRef) ? key : value, queue);
+            this.hash = hash;
+            this.hard = (keyRef) ? value : key;
+            this.keyRef = keyRef;
+            this.next = next;
+        }
+
+        public int getHash() {
+            return hash;
+        }
+
+        public Entry getNext() {
+            return next;
+        }
+
+        public void setNext(Entry next) {
+            this.next = next;
+        }
+
+        public Object clone(ReferenceQueue queue) {
+            // It is the callers responsibility to set the next field
+            // correctly.
+            return new SoftEntry(hash, getKey(), getValue(), keyRef, null, queue);
+        }
+
+        // Map.Entry Ops 
+        public Object getKey() {
+            return (keyRef) ? super.get() : hard;
+        }
+
+        public Object getValue() {
+            return (keyRef) ? hard : super.get();
+        }
+
+        public Object setValue(Object value) {
+            if (!keyRef) {
+                throw new Error("Attempt to reset reference value.");
+            }
+
+            Object oldValue = hard;
+            hard = value;
+
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+
+            Map.Entry e = (Map.Entry) o;
+
+            return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue());
+        }
+
+        public int hashCode() {
+            Object val = getValue();
+
+            return hash ^ ((val == null) ? 0 : val.hashCode());
+        }
+
+        public String toString() {
+            return getKey() + "=" + getValue();
+        }
+    }
+
+    /**
+          * Map iterator.
+     */
+    private class HashIterator implements Iterator {
+        final Entry[] table = ConcurrentReferenceHashMap.this.table;
+        final int type;
+        int startIndex;
+        int stopIndex = 0;
+        int index;
+        Entry entry = null;
+        Entry lastReturned = null;
+
+        HashIterator(int type, int startIndex) {
+            this.type = type;
+            this.startIndex = startIndex;
+            index = startIndex;
+        }
+
+        public boolean hasNext() {
+            if (entry != null) {
+                return true;
+            }
+
+            while (index >= stopIndex) {
+                if ((entry = table[index--]) != null) {
+                    return true;
+                }
+            }
+
+            if (stopIndex == 0) {
+                index = table.length - 1;
+                stopIndex = startIndex + 1;
+
+                while (index >= stopIndex) {
+                    if ((entry = table[index--]) != null) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        public Object next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            Entry e = lastReturned = entry;
+            entry = e.getNext();
+
+            return (type == KEYS) ? e.getKey()
+                                  : ((type == VALUES) ? e.getValue() : e);
+        }
+
+        public void remove() {
+            if (lastReturned == null) {
+                throw new IllegalStateException();
+            }
+
+            synchronized (ConcurrentReferenceHashMap.this) {
+                Entry[] tab = ConcurrentReferenceHashMap.this.table;
+                int index = (lastReturned.getHash() & 0x7FFFFFFF) % tab.length;
+
+                for (Entry e = tab[index], prev = null; e != null;
+                        prev = e, e = e.getNext()) {
+                    if (e == lastReturned) {
+                        if (prev == null) {
+                            tab[index] = e.getNext();
+                        } else {
+                            prev.setNext(e.getNext());
+                        }
+
+                        count--;
+                        lastReturned = null;
+
+                        return;
+                    }
+                }
+
+                throw new Error("Iterated off table when doing remove");
+            }
+        }
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashMap.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import org.apache.commons.collections.map.*;
+import org.apache.commons.collections.set.*;
+
+import java.io.*;
+
+import java.util.*;
+
+
+/**
+ *  <p>A concurrent set whose values may be stored as weak or soft
+ *  references.</p>
+ *
+ *  @author Abe White
+ *  @nojavadoc */
+public class ConcurrentReferenceHashSet implements Set, Serializable {
+    /**
+     *  Hard reference marker.
+      */
+    public static final int HARD = 0;
+
+    /**
+     *  Soft reference marker.
+      */
+    public static final int SOFT = 1;
+
+    /**
+     *  Weak reference marker.
+     */
+    public static final int WEAK = 2;
+    private static final Object DUMMY_VAL = new Object();
+    private final Set _set;
+
+    /**
+     *  Construct a set with the given reference type.
+     */
+    public ConcurrentReferenceHashSet(int refType) {
+        if (refType == HARD) {
+            _set = MapBackedSet.decorate(new ConcurrentHashMap(), DUMMY_VAL);
+        } else {
+            int mapRefType = (refType == WEAK)
+                ? ConcurrentReferenceHashMap.WEAK
+                : ConcurrentReferenceHashMap.SOFT;
+            _set = MapBackedSet.decorate(new ConcurrentReferenceHashMap(
+                        mapRefType, ConcurrentReferenceHashMap.HARD), DUMMY_VAL);
+        }
+    }
+
+    public boolean add(Object obj) {
+        return _set.add(obj);
+    }
+
+    public boolean addAll(Collection coll) {
+        return _set.addAll(coll);
+    }
+
+    public void clear() {
+        _set.clear();
+    }
+
+    public boolean contains(Object obj) {
+        return _set.contains(obj);
+    }
+
+    public boolean containsAll(Collection coll) {
+        return _set.containsAll(coll);
+    }
+
+    public boolean isEmpty() {
+        return _set.isEmpty();
+    }
+
+    public Iterator iterator() {
+        return _set.iterator();
+    }
+
+    public boolean remove(Object obj) {
+        return _set.remove(obj);
+    }
+
+    public boolean removeAll(Collection coll) {
+        return _set.removeAll(coll);
+    }
+
+    public boolean retainAll(Collection coll) {
+        return _set.retainAll(coll);
+    }
+
+    public int size() {
+        return _set.size();
+    }
+
+    public Object[] toArray() {
+        return _set.toArray();
+    }
+
+    public Object[] toArray(Object[] arr) {
+        return _set.toArray(arr);
+    }
+
+    public int hashCode() {
+        return _set.hashCode();
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof ConcurrentReferenceHashSet) {
+            obj = ((ConcurrentReferenceHashSet) obj)._set;
+        }
+
+        return _set.equals(obj);
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/ConcurrentReferenceHashSet.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Originally written by Doug Lea and released into the public domain.
+ * This may be used for any purposes whatsoever without acknowledgment.
+ * Thanks for the assistance and support of Sun Microsystems Labs,
+ * and everyone contributing, testing, and using this code.
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import java.util.*;
+
+
+class CondVar implements Condition, java.io.Serializable {
+    /** The lock */
+    protected final ExclusiveLock lock;
+
+    /**
+     * Create a new CondVar that relies on the given mutual
+     * exclusion lock.
+     * @param lock A non-reentrant mutual exclusion lock.
+     */
+    CondVar(ExclusiveLock lock) {
+        this.lock = lock;
+    }
+
+    public void awaitUninterruptibly() {
+        int holdCount = lock.getHoldCount();
+
+        if (holdCount == 0) {
+            throw new IllegalMonitorStateException();
+        }
+
+        // avoid instant spurious wakeup if thread already interrupted
+        boolean wasInterrupted = Thread.interrupted();
+
+        try {
+            synchronized (this) {
+                for (int i = holdCount; i > 0; i--)
+                    lock.unlock();
+
+                while (true) {
+                    try {
+                        wait();
+
+                        break;
+                    } catch (InterruptedException ex) {
+                        wasInterrupted = true;
+                        // may have masked the signal and there is no way
+                        // to tell; defensively propagate the signal
+                        notify();
+                    }
+                }
+            }
+        } finally {
+            for (int i = holdCount; i > 0; i--)
+                lock.lock();
+
+            if (wasInterrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    public void await() throws InterruptedException {
+        int holdCount = lock.getHoldCount();
+
+        if (holdCount == 0) {
+            throw new IllegalMonitorStateException();
+        }
+
+        if (Thread.interrupted()) {
+            throw new InterruptedException();
+        }
+
+        try {
+            synchronized (this) {
+                for (int i = holdCount; i > 0; i--)
+                    lock.unlock();
+
+                try {
+                    wait();
+                } catch (InterruptedException ex) {
+                    notify();
+                    throw ex;
+                }
+            }
+        } finally {
+            for (int i = holdCount; i > 0; i--)
+                lock.lock();
+        }
+    }
+
+    public boolean await(long timeout, TimeUnit unit)
+        throws InterruptedException {
+        int holdCount = lock.getHoldCount();
+
+        if (holdCount == 0) {
+            throw new IllegalMonitorStateException();
+        }
+
+        if (Thread.interrupted()) {
+            throw new InterruptedException();
+        }
+
+        long nanos = unit.toNanos(timeout);
+        boolean success = false;
+
+        try {
+            synchronized (this) {
+                for (int i = holdCount; i > 0; i--)
+                    lock.unlock();
+
+                try {
+                    if (nanos > 0) {
+                        long start = Utils.nanoTime();
+                        TimeUnit.NANOSECONDS.timedWait(this, nanos);
+                        // DK: due to coarse-grained (millis) clock, it seems
+                        // preferable to acknowledge timeout (success == false)
+                        // when the equality holds (timing is exact)
+                        success = (Utils.nanoTime() - start) < nanos;
+                    }
+                } catch (InterruptedException ex) {
+                    notify();
+                    throw ex;
+                }
+            }
+        } finally {
+            for (int i = holdCount; i > 0; i--)
+                lock.lock();
+        }
+
+        return success;
+    }
+
+    //    public long awaitNanos(long timeout) throws InterruptedException {
+    //        throw new UnsupportedOperationException();
+    //    }
+    public boolean awaitUntil(Date deadline) throws InterruptedException {
+        if (deadline == null) {
+            throw new NullPointerException();
+        }
+
+        int holdCount = lock.getHoldCount();
+
+        if (holdCount == 0) {
+            throw new IllegalMonitorStateException();
+        }
+
+        long abstime = deadline.getTime();
+
+        if (Thread.interrupted()) {
+            throw new InterruptedException();
+        }
+
+        boolean success = false;
+
+        try {
+            synchronized (this) {
+                for (int i = holdCount; i > 0; i--)
+                    lock.unlock();
+
+                try {
+                    long start = System.currentTimeMillis();
+                    long msecs = abstime - start;
+
+                    if (msecs > 0) {
+                        wait(msecs);
+                        // DK: due to coarse-grained (millis) clock, it seems
+                        // preferable to acknowledge timeout (success == false)
+                        // when the equality holds (timing is exact)
+                        success = (System.currentTimeMillis() - start) < msecs;
+                    }
+                } catch (InterruptedException ex) {
+                    notify();
+                    throw ex;
+                }
+            }
+        } finally {
+            for (int i = holdCount; i > 0; i--)
+                lock.lock();
+        }
+
+        return success;
+    }
+
+    public synchronized void signal() {
+        if (!lock.isHeldByCurrentThread()) {
+            throw new IllegalMonitorStateException();
+        }
+
+        notify();
+    }
+
+    public synchronized void signalAll() {
+        if (!lock.isHeldByCurrentThread()) {
+            throw new IllegalMonitorStateException();
+        }
+
+        notifyAll();
+    }
+
+    protected ExclusiveLock getLock() {
+        return lock;
+    }
+
+    protected boolean hasWaiters() {
+        throw new UnsupportedOperationException("Use FAIR version");
+    }
+
+    protected int getWaitQueueLength() {
+        throw new UnsupportedOperationException("Use FAIR version");
+    }
+
+    protected Collection getWaitingThreads() {
+        throw new UnsupportedOperationException("Use FAIR version");
+    }
+
+    static interface ExclusiveLock extends Lock {
+        boolean isHeldByCurrentThread();
+
+        int getHoldCount();
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CondVar.java
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import java.util.Date;
+
+
+/**
+ * <tt>Condition</tt> factors out the <tt>Object</tt> monitor
+ * methods ({@link Object#wait() wait}, {@link Object#notify notify}
+ * and {@link Object#notifyAll notifyAll}) into distinct objects to
+ * give the effect of having multiple wait-sets per object, by
+ * combining them with the use of arbitrary {@link Lock} implementations.
+ * Where a <tt>Lock</tt> replaces the use of <tt>synchronized</tt> methods
+ * and statements, a <tt>Condition</tt> replaces the use of the Object
+ * monitor methods.
+ *
+ * <p>Conditions (also known as <em>condition queues</em> or
+ * <em>condition variables</em>) provide a means for one thread to
+ * suspend execution (to &quot;wait&quot;) until notified by another
+ * thread that some state condition may now be true.  Because access
+ * to this shared state information occurs in different threads, it
+ * must be protected, so a lock of some form is associated with the
+ * condition. The key property that waiting for a condition provides
+ * is that it <em>atomically</em> releases the associated lock and
+ * suspends the current thread, just like <tt>Object.wait</tt>.
+ *
+ * <p>A <tt>Condition</tt> instance is intrinsically bound to a lock.
+ * To obtain a <tt>Condition</tt> instance for a particular {@link Lock}
+ * instance use its {@link Lock#newCondition newCondition()} method.
+ *
+ * <p>As an example, suppose we have a bounded buffer which supports
+ * <tt>put</tt> and <tt>take</tt> methods.  If a
+ * <tt>take</tt> is attempted on an empty buffer, then the thread will block
+ * until an item becomes available; if a <tt>put</tt> is attempted on a
+ * full buffer, then the thread will block until a space becomes available.
+ * We would like to keep waiting <tt>put</tt> threads and <tt>take</tt>
+ * threads in separate wait-sets so that we can use the optimization of
+ * only notifying a single thread at a time when items or spaces become
+ * available in the buffer. This can be achieved using two
+ * {@link Condition} instances.
+ * <pre>
+ * class BoundedBuffer {
+ *   <b>final Lock lock = new ReentrantLock();</b>
+ *   final Condition notFull  = <b>lock.newCondition(); </b>
+ *   final Condition notEmpty = <b>lock.newCondition(); </b>
+ *
+ *   final Object[] items = new Object[100];
+ *   int putptr, takeptr, count;
+ *
+ *   public void put(Object x) throws InterruptedException {
+ *     <b>lock.lock();
+ *     try {</b>
+ *       while (count == items.length)
+ *         <b>notFull.await();</b>
+ *       items[putptr] = x;
+ *       if (++putptr == items.length) putptr = 0;
+ *       ++count;
+ *       <b>notEmpty.signal();</b>
+ *     <b>} finally {
+ *       lock.unlock();
+ *     }</b>
+ *   }
+ *
+ *   public Object take() throws InterruptedException {
+ *     <b>lock.lock();
+ *     try {</b>
+ *       while (count == 0)
+ *         <b>notEmpty.await();</b>
+ *       Object x = items[takeptr];
+ *       if (++takeptr == items.length) takeptr = 0;
+ *       --count;
+ *       <b>notFull.signal();</b>
+ *       return x;
+ *     <b>} finally {
+ *       lock.unlock();
+ *     }</b>
+ *   }
+ * }
+ * </pre>
+ *
+ * (The {@link edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue} class provides
+ * this functionality, so there is no reason to implement this
+ * sample usage class.)
+ *
+ * <p>A <tt>Condition</tt> implementation can provide behavior and semantics
+ * that is
+ * different from that of the <tt>Object</tt> monitor methods, such as
+ * guaranteed ordering for notifications, or not requiring a lock to be held
+ * when performing notifications.
+ * If an implementation provides such specialized semantics then the
+ * implementation must document those semantics.
+ *
+ * <p>Note that <tt>Condition</tt> instances are just normal objects and can
+ * themselves be used as the target in a <tt>synchronized</tt> statement,
+ * and can have their own monitor {@link Object#wait wait} and
+ * {@link Object#notify notification} methods invoked.
+ * Acquiring the monitor lock of a <tt>Condition</tt> instance, or using its
+ * monitor methods, has no specified relationship with acquiring the
+ * {@link Lock} associated with that <tt>Condition</tt> or the use of its
+ * {@link #await waiting} and {@link #signal signalling} methods.
+ * It is recommended that to avoid confusion you never use <tt>Condition</tt>
+ * instances in this way, except perhaps within their own implementation.
+ *
+ * <p>Except where noted, passing a <tt>null</tt> value for any parameter
+ * will result in a {@link NullPointerException} being thrown.
+ *
+ * <h3>Implementation Considerations</h3>
+ *
+ * <p>When waiting upon a <tt>Condition</tt>, a &quot;<em>spurious
+ * wakeup</em>&quot; is permitted to occur, in
+ * general, as a concession to the underlying platform semantics.
+ * This has little practical impact on most application programs as a
+ * <tt>Condition</tt> should always be waited upon in a loop, testing
+ * the state predicate that is being waited for.  An implementation is
+ * free to remove the possibility of spurious wakeups but it is
+ * recommended that applications programmers always assume that they can
+ * occur and so always wait in a loop.
+ *
+ * <p>The three forms of condition waiting
+ * (interruptible, non-interruptible, and timed) may differ in their ease of
+ * implementation on some platforms and in their performance characteristics.
+ * In particular, it may be difficult to provide these features and maintain
+ * specific semantics such as ordering guarantees.
+ * Further, the ability to interrupt the actual suspension of the thread may
+ * not always be feasible to implement on all platforms.
+ * <p>Consequently, an implementation is not required to define exactly the
+ * same guarantees or semantics for all three forms of waiting, nor is it
+ * required to support interruption of the actual suspension of the thread.
+ * <p>An implementation is required to
+ * clearly document the semantics and guarantees provided by each of the
+ * waiting methods, and when an implementation does support interruption of
+ * thread suspension then it must obey the interruption semantics as defined
+ * in this interface.
+ * <p>As interruption generally implies cancellation, and checks for
+ * interruption are often infrequent, an implementation can favor responding
+ * to an interrupt over normal method return. This is true even if it can be
+ * shown that the interrupt occurred after another action may have unblocked
+ * the thread. An implementation should document this behavior.
+ *
+ *
+ * @since 1.5
+ * @author Doug Lea
+ */
+public interface Condition {
+    /**
+     * Causes the current thread to wait until it is signalled or
+     * {@link Thread#interrupt interrupted}.
+     *
+     * <p>The lock associated with this <tt>Condition</tt> is atomically
+     * released and the current thread becomes disabled for thread scheduling
+     * purposes and lies dormant until <em>one</em> of four things happens:
+     * <ul>
+     * <li>Some other thread invokes the {@link #signal} method for this
+     * <tt>Condition</tt> and the current thread happens to be chosen as the
+     * thread to be awakened; or
+     * <li>Some other thread invokes the {@link #signalAll} method for this
+     * <tt>Condition</tt>; or
+     * <li>Some other thread {@link Thread#interrupt interrupts} the current
+     * thread, and interruption of thread suspension is supported; or
+     * <li>A &quot;<em>spurious wakeup</em>&quot; occurs
+     * </ul>
+     *
+     * <p>In all cases, before this method can return the current thread must
+     * re-acquire the lock associated with this condition. When the
+     * thread returns it is <em>guaranteed</em> to hold this lock.
+     *
+     * <p>If the current thread:
+     * <ul>
+     * <li>has its interrupted status set on entry to this method; or
+     * <li>is {@link Thread#interrupt interrupted} while waiting
+     * and interruption of thread suspension is supported,
+     * </ul>
+     * then {@link InterruptedException} is thrown and the current thread's
+     * interrupted status is cleared. It is not specified, in the first
+     * case, whether or not the test for interruption occurs before the lock
+     * is released.
+     *
+     * <p><b>Implementation Considerations</b>
+     * <p>The current thread is assumed to hold the lock associated with this
+     * <tt>Condition</tt> when this method is called.
+     * It is up to the implementation to determine if this is
+     * the case and if not, how to respond. Typically, an exception will be
+     * thrown (such as {@link IllegalMonitorStateException}) and the
+     * implementation must document that fact.
+     *
+     * <p>An implementation can favor responding to an interrupt over normal
+     * method return in response to a signal. In that case the implementation
+     * must ensure that the signal is redirected to another waiting thread, if
+     * there is one.
+     *
+     * @throws InterruptedException if the current thread is interrupted (and
+     * interruption of thread suspension is supported).
+     */
+    void await() throws InterruptedException;
+
+    /**
+     * Causes the current thread to wait until it is signalled.
+     *
+     * <p>The lock associated with this condition is atomically
+     * released and the current thread becomes disabled for thread scheduling
+     * purposes and lies dormant until <em>one</em> of three things happens:
+     * <ul>
+     * <li>Some other thread invokes the {@link #signal} method for this
+     * <tt>Condition</tt> and the current thread happens to be chosen as the
+     * thread to be awakened; or
+     * <li>Some other thread invokes the {@link #signalAll} method for this
+     * <tt>Condition</tt>; or
+     * <li>A &quot;<em>spurious wakeup</em>&quot; occurs
+     * </ul>
+     *
+     * <p>In all cases, before this method can return the current thread must
+     * re-acquire the lock associated with this condition. When the
+     * thread returns it is <em>guaranteed</em> to hold this lock.
+     *
+     * <p>If the current thread's interrupted status is set when it enters
+     * this method, or it is {@link Thread#interrupt interrupted}
+     * while waiting, it will continue to wait until signalled. When it finally
+     * returns from this method its interrupted status will still
+     * be set.
+     *
+     * <p><b>Implementation Considerations</b>
+     * <p>The current thread is assumed to hold the lock associated with this
+     * <tt>Condition</tt> when this method is called.
+     * It is up to the implementation to determine if this is
+     * the case and if not, how to respond. Typically, an exception will be
+     * thrown (such as {@link IllegalMonitorStateException}) and the
+     * implementation must document that fact.
+     */
+    void awaitUninterruptibly();
+
+    //    /**
+    //     * Causes the current thread to wait until it is signalled or interrupted,
+    //     * or the specified waiting time elapses.
+    //     *
+    //     * <p>The lock associated with this condition is atomically
+    //     * released and the current thread becomes disabled for thread scheduling
+    //     * purposes and lies dormant until <em>one</em> of five things happens:
+    //     * <ul>
+    //     * <li>Some other thread invokes the {@link #signal} method for this
+    //     * <tt>Condition</tt> and the current thread happens to be chosen as the
+    //     * thread to be awakened; or
+    //     * <li>Some other thread invokes the {@link #signalAll} method for this
+    //     * <tt>Condition</tt>; or
+    //     * <li>Some other thread {@link Thread#interrupt interrupts} the current
+    //     * thread, and interruption of thread suspension is supported; or
+    //     * <li>The specified waiting time elapses; or
+    //     * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
+    //     * </ul>
+    //     *
+    //     * <p>In all cases, before this method can return the current thread must
+    //     * re-acquire the lock associated with this condition. When the
+    //     * thread returns it is <em>guaranteed</em> to hold this lock.
+    //     *
+    //     * <p>If the current thread:
+    //     * <ul>
+    //     * <li>has its interrupted status set on entry to this method; or
+    //     * <li>is {@link Thread#interrupt interrupted} while waiting
+    //     * and interruption of thread suspension is supported,
+    //     * </ul>
+    //     * then {@link InterruptedException} is thrown and the current thread's
+    //     * interrupted status is cleared. It is not specified, in the first
+    //     * case, whether or not the test for interruption occurs before the lock
+    //     * is released.
+    //     *
+    //     * <p>The method returns an estimate of the number of nanoseconds
+    //     * remaining to wait given the supplied <tt>nanosTimeout</tt>
+    //     * value upon return, or a value less than or equal to zero if it
+    //     * timed out. This value can be used to determine whether and how
+    //     * long to re-wait in cases where the wait returns but an awaited
+    //     * condition still does not hold. Typical uses of this method take
+    //     * the following form:
+    //     *
+    //     * <pre>
+    //     * synchronized boolean aMethod(long timeout, TimeUnit unit) {
+    //     *   long nanosTimeout = unit.toNanos(timeout);
+    //     *   while (!conditionBeingWaitedFor) {
+    //     *     if (nanosTimeout &gt; 0)
+    //     *         nanosTimeout = theCondition.awaitNanos(nanosTimeout);
+    //     *      else
+    //     *        return false;
+    //     *   }
+    //     *   // ...
+    //     * }
+    //     * </pre>
+    //     *
+    //     * <p> Design note: This method requires a nanosecond argument so
+    //     * as to avoid truncation errors in reporting remaining times.
+    //     * Such precision loss would make it difficult for programmers to
+    //     * ensure that total waiting times are not systematically shorter
+    //     * than specified when re-waits occur.
+    //     *
+    //     * <p><b>Implementation Considerations</b>
+    //     * <p>The current thread is assumed to hold the lock associated with this
+    //     * <tt>Condition</tt> when this method is called.
+    //     * It is up to the implementation to determine if this is
+    //     * the case and if not, how to respond. Typically, an exception will be
+    //     * thrown (such as {@link IllegalMonitorStateException}) and the
+    //     * implementation must document that fact.
+    //     *
+    //     * <p>An implementation can favor responding to an interrupt over normal
+    //     * method return in response to a signal, or over indicating the elapse
+    //     * of the specified waiting time. In either case the implementation
+    //     * must ensure that the signal is redirected to another waiting thread, if
+    //     * there is one.
+    //     *
+    //     * @param nanosTimeout the maximum time to wait, in nanoseconds
+    //     * @return A value less than or equal to zero if the wait has
+    //     * timed out; otherwise an estimate, that
+    //     * is strictly less than the <tt>nanosTimeout</tt> argument,
+    //     * of the time still remaining when this method returned.
+    //     *
+    //     * @throws InterruptedException if the current thread is interrupted (and
+    //     * interruption of thread suspension is supported).
+    //     */
+    //    long awaitNanos(long nanosTimeout) throws InterruptedException;
+
+    /**
+     * Causes the current thread to wait until it is signalled or interrupted,
+     * or the specified waiting time elapses. This method is behaviorally
+     * equivalent to:<br>
+     * <pre>
+     *   awaitNanos(unit.toNanos(time)) &gt; 0
+     * </pre>
+     * @param time the maximum time to wait
+     * @param unit the time unit of the <tt>time</tt> argument.
+     * @return <tt>false</tt> if the waiting time detectably elapsed
+     * before return from the method, else <tt>true</tt>.
+     * @throws InterruptedException if the current thread is interrupted (and
+     * interruption of thread suspension is supported).
+     */
+    boolean await(long time, TimeUnit unit) throws InterruptedException;
+
+    /**
+     * Causes the current thread to wait until it is signalled or interrupted,
+     * or the specified deadline elapses.
+     *
+     * <p>The lock associated with this condition is atomically
+     * released and the current thread becomes disabled for thread scheduling
+     * purposes and lies dormant until <em>one</em> of five things happens:
+     * <ul>
+     * <li>Some other thread invokes the {@link #signal} method for this
+     * <tt>Condition</tt> and the current thread happens to be chosen as the
+     * thread to be awakened; or
+     * <li>Some other thread invokes the {@link #signalAll} method for this
+     * <tt>Condition</tt>; or
+     * <li>Some other thread {@link Thread#interrupt interrupts} the current
+     * thread, and interruption of thread suspension is supported; or
+     * <li>The specified deadline elapses; or
+     * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
+     * </ul>
+     *
+     * <p>In all cases, before this method can return the current thread must
+     * re-acquire the lock associated with this condition. When the
+     * thread returns it is <em>guaranteed</em> to hold this lock.
+     *
+     *
+     * <p>If the current thread:
+     * <ul>
+     * <li>has its interrupted status set on entry to this method; or
+     * <li>is {@link Thread#interrupt interrupted} while waiting
+     * and interruption of thread suspension is supported,
+     * </ul>
+     * then {@link InterruptedException} is thrown and the current thread's
+     * interrupted status is cleared. It is not specified, in the first
+     * case, whether or not the test for interruption occurs before the lock
+     * is released.
+     *
+     *
+     * <p>The return value indicates whether the deadline has elapsed,
+     * which can be used as follows:
+     * <pre>
+     * synchronized boolean aMethod(Date deadline) {
+     *   boolean stillWaiting = true;
+     *   while (!conditionBeingWaitedFor) {
+     *     if (stillWaiting)
+     *         stillWaiting = theCondition.awaitUntil(deadline);
+     *      else
+     *        return false;
+     *   }
+     *   // ...
+     * }
+     * </pre>
+     *
+     * <p><b>Implementation Considerations</b>
+     * <p>The current thread is assumed to hold the lock associated with this
+     * <tt>Condition</tt> when this method is called.
+     * It is up to the implementation to determine if this is
+     * the case and if not, how to respond. Typically, an exception will be
+     * thrown (such as {@link IllegalMonitorStateException}) and the
+     * implementation must document that fact.
+     *
+     * <p>An implementation can favor responding to an interrupt over normal
+     * method return in response to a signal, or over indicating the passing
+     * of the specified deadline. In either case the implementation
+     * must ensure that the signal is redirected to another waiting thread, if
+     * there is one.
+     *
+     *
+     * @param deadline the absolute time to wait until
+     * @return <tt>false</tt> if the deadline has
+     * elapsed upon return, else <tt>true</tt>.
+     *
+     * @throws InterruptedException if the current thread is interrupted (and
+     * interruption of thread suspension is supported).
+     */
+    boolean awaitUntil(Date deadline) throws InterruptedException;
+
+    /**
+     * Wakes up one waiting thread.
+     *
+     * <p>If any threads are waiting on this condition then one
+     * is selected for waking up. That thread must then re-acquire the
+     * lock before returning from <tt>await</tt>.
+     */
+    void signal();
+
+    /**
+     * Wakes up all waiting threads.
+     *
+     * <p>If any threads are waiting on this condition then they are
+     * all woken up. Each thread must re-acquire the lock before it can
+     * return from <tt>await</tt>.
+     */
+    void signalAll();
+}

Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Condition.java
------------------------------------------------------------------------------
    svn:executable = *



Mime
View raw message