You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/06/28 21:34:40 UTC
svn commit: r417856 [20/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...
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,1248 @@
+/*
+ * 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 Dawid Kurzyniec, on the basis of public specifications and
+ * public domain sources from JSR 166, and released to the public domain,
+ * as explained at http://creativecommons.org/licenses/publicdomain.
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.lang.reflect.Array;
+
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+
+public class CopyOnWriteArrayList implements List, Cloneable, Serializable {
+ private static final long serialVersionUID = 8673264195747942595L;
+ private volatile transient Object[] array;
+
+ public CopyOnWriteArrayList() {
+ setArray(new Object[0]);
+ }
+
+ public CopyOnWriteArrayList(Collection c) {
+ // must deal with concurrent collections
+ Object[] array = c.toArray();
+
+ // make sure the array is Object[] type
+ if (array.getClass() != Object[].class) {
+ array = Arrays.copyOf(array, array.length, Object[].class);
+ }
+
+ // assume that c.toArray() has returned a new array instance, as
+ // required by the spec
+ setArray(array);
+ }
+
+ public CopyOnWriteArrayList(Object[] array) {
+ setArray(Arrays.copyOf(array, array.length, Object[].class));
+ }
+
+ final Object[] getArray() {
+ return array;
+ }
+
+ final void setArray(Object[] array) {
+ this.array = array;
+ }
+
+ public int size() {
+ return getArray().length;
+ }
+
+ public boolean isEmpty() {
+ return getArray().length == 0;
+ }
+
+ private static int search(Object[] array, Object subject, int pos, int end) {
+ if (subject == null) {
+ for (; pos < end; pos++) {
+ if (array[pos] == null) {
+ return pos;
+ }
+ }
+ } else {
+ for (; pos < end; pos++) {
+ if (subject.equals(array[pos])) {
+ return pos;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ private static int reverseSearch(Object[] array, Object subject, int start,
+ int pos) {
+ if (subject == null) {
+ for (pos--; pos >= start; pos--) {
+ if (array[pos] == null) {
+ return pos;
+ }
+ }
+ } else {
+ for (pos--; pos >= start; pos--) {
+ if (subject.equals(array[pos])) {
+ return pos;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ public boolean contains(Object o) {
+ Object[] array = getArray();
+
+ return search(array, o, 0, array.length) >= 0;
+ }
+
+ public Iterator iterator() {
+ return new COWIterator(getArray(), 0);
+ }
+
+ public Object[] toArray() {
+ Object[] array = getArray();
+
+ return Arrays.copyOf(array, array.length, Object[].class);
+ }
+
+ public Object[] toArray(Object[] a) {
+ Object[] array = getArray();
+ int length = array.length;
+
+ if (a.length < length) {
+ return Arrays.copyOf(array, length, a.getClass());
+ } else {
+ System.arraycopy(array, 0, a, 0, length);
+
+ if (a.length > length) {
+ a[length] = null;
+ }
+
+ return a;
+ }
+ }
+
+ public boolean add(Object o) {
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+ Object[] newarr = new Object[length + 1];
+ System.arraycopy(oldarr, 0, newarr, 0, length);
+ newarr[length] = o;
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public boolean addIfAbsent(Object o) {
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+
+ if (search(array, o, 0, length) >= 0) {
+ return false;
+ }
+
+ Object[] newarr = new Object[length + 1];
+ System.arraycopy(oldarr, 0, newarr, 0, length);
+ newarr[length] = o;
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public int addAllAbsent(Collection c) {
+ Object[] arr = c.toArray();
+
+ if (arr.length == 0) {
+ return 0;
+ }
+
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int oldlength = oldarr.length;
+ Object[] tmp = new Object[arr.length];
+ int added = 0;
+
+ for (int i = 0; i < arr.length; i++) {
+ Object o = arr[i];
+
+ if ((search(oldarr, o, 0, oldlength) < 0) &&
+ (search(tmp, o, 0, added) < 0)) {
+ tmp[added++] = o;
+ }
+ }
+
+ if (added == 0) {
+ return 0;
+ }
+
+ Object[] newarr = new Object[oldlength + added];
+ System.arraycopy(oldarr, 0, newarr, 0, oldlength);
+ System.arraycopy(tmp, 0, newarr, oldlength, added);
+ setArray(newarr);
+
+ return added;
+ }
+ }
+
+ public boolean remove(Object o) {
+ synchronized (this) {
+ Object[] array = getArray();
+ int length = array.length;
+ int pos = search(array, o, 0, length);
+
+ if (pos < 0) {
+ return false;
+ }
+
+ Object[] newarr = new Object[length - 1];
+ int moved = length - pos - 1;
+
+ if (pos > 0) {
+ System.arraycopy(array, 0, newarr, 0, pos);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, pos + 1, newarr, pos, moved);
+ }
+
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public boolean containsAll(Collection c) {
+ Object[] array = getArray();
+
+ for (Iterator itr = c.iterator(); itr.hasNext();) {
+ if (search(array, itr.next(), 0, array.length) < 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean addAll(Collection c) {
+ // must deal with concurrent collections
+ Object[] ca = c.toArray();
+
+ if (ca.length == 0) {
+ return false;
+ }
+
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+ Object[] newarr = new Object[length + ca.length];
+ System.arraycopy(oldarr, 0, newarr, 0, length);
+
+ int pos = length;
+ System.arraycopy(ca, 0, newarr, pos, ca.length);
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public boolean addAll(int index, Collection c) {
+ // must deal with concurrent collections
+ Object[] ca = c.toArray();
+
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+
+ if ((index < 0) || (index > length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ if (ca.length == 0) {
+ return false;
+ }
+
+ Object[] newarr = new Object[length + ca.length];
+ int moved = length - index;
+ System.arraycopy(oldarr, 0, newarr, 0, index);
+
+ int pos = length;
+ System.arraycopy(ca, 0, newarr, index, ca.length);
+
+ if (moved > 0) {
+ System.arraycopy(oldarr, index, newarr, index + ca.length, moved);
+ }
+
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public boolean removeAll(Collection c) {
+ if (c.isEmpty()) {
+ return false;
+ }
+
+ synchronized (this) {
+ Object[] array = getArray();
+ int length = array.length;
+ Object[] tmp = new Object[length];
+ int newlen = 0;
+
+ for (int i = 0; i < length; i++) {
+ Object o = array[i];
+
+ if (!c.contains(o)) {
+ tmp[newlen++] = o;
+ }
+ }
+
+ if (newlen == length) {
+ return false;
+ }
+
+ Object[] newarr = new Object[newlen];
+ System.arraycopy(tmp, 0, newarr, 0, newlen);
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public boolean retainAll(Collection c) {
+ synchronized (this) {
+ Object[] array = getArray();
+ int length = array.length;
+ Object[] tmp = new Object[length];
+ int newlen = 0;
+
+ for (int i = 0; i < length; i++) {
+ Object o = array[i];
+
+ if (c.contains(o)) {
+ tmp[newlen++] = o;
+ }
+ }
+
+ if (newlen == length) {
+ return false;
+ }
+
+ Object[] newarr = new Object[newlen];
+ System.arraycopy(tmp, 0, newarr, 0, newlen);
+ setArray(newarr);
+
+ return true;
+ }
+ }
+
+ public void clear() {
+ setArray(new Object[0]);
+ }
+
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof List)) {
+ return false;
+ }
+
+ ListIterator itr = ((List) o).listIterator();
+ Object[] array = getArray();
+ int length = array.length;
+ int idx = 0;
+
+ while ((idx < length) && itr.hasNext()) {
+ Object o1 = array[idx++];
+ Object o2 = itr.next();
+
+ if (!eq(o1, o2)) {
+ return false;
+ }
+ }
+
+ return ((idx == length) && !itr.hasNext());
+ }
+
+ public int hashCode() {
+ int hashCode = 1;
+ Object[] array = getArray();
+ int length = array.length;
+
+ for (int i = 0; i < length; i++) {
+ Object o = array[i];
+ hashCode = (31 * hashCode) + ((o == null) ? 0 : o.hashCode());
+ }
+
+ return hashCode;
+ }
+
+ public Object get(int index) {
+ return getArray()[index];
+ }
+
+ public Object set(int index, Object element) {
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+
+ // piggyback the array bounds check
+ Object oldVal = oldarr[index];
+
+ if (oldVal == element) {
+ setArray(oldarr);
+ } else {
+ Object[] newarr = new Object[length];
+ System.arraycopy(oldarr, 0, newarr, 0, length);
+ newarr[index] = element;
+ setArray(newarr);
+ }
+
+ return oldVal;
+ }
+ }
+
+ public void add(int index, Object element) {
+ synchronized (this) {
+ Object[] oldarr = getArray();
+ int length = oldarr.length;
+
+ if ((index < 0) || (index > length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] newarr = new Object[length + 1];
+ int moved = length - index;
+ System.arraycopy(oldarr, 0, newarr, 0, index);
+ newarr[index] = element;
+
+ if (moved > 0) {
+ System.arraycopy(oldarr, index, newarr, index + 1, moved);
+ }
+
+ setArray(newarr);
+ }
+ }
+
+ public Object remove(int index) {
+ synchronized (this) {
+ Object[] array = getArray();
+ int length = array.length;
+
+ if ((index < 0) || (index >= length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object result = array[index];
+ Object[] newarr = new Object[length - 1];
+ int moved = length - index - 1;
+
+ if (index > 0) {
+ System.arraycopy(array, 0, newarr, 0, index);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, index + 1, newarr, index, moved);
+ }
+
+ setArray(newarr);
+
+ return result;
+ }
+ }
+
+ public int indexOf(Object o) {
+ Object[] array = getArray();
+
+ return search(array, o, 0, array.length);
+ }
+
+ public int indexOf(Object o, int index) {
+ Object[] array = getArray();
+
+ return search(array, o, index, array.length);
+ }
+
+ public int lastIndexOf(Object o) {
+ Object[] array = getArray();
+
+ return reverseSearch(array, o, 0, array.length);
+ }
+
+ public int lastIndexOf(Object o, int index) {
+ Object[] array = getArray();
+
+ return reverseSearch(array, o, 0, index);
+ }
+
+ public ListIterator listIterator() {
+ return new COWIterator(getArray(), 0);
+ }
+
+ public ListIterator listIterator(int index) {
+ Object[] array = getArray();
+
+ if ((index < 0) || (index > array.length)) {
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: " +
+ array.length);
+ }
+
+ return new COWIterator(array, index);
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ Object[] array = getArray();
+
+ if ((fromIndex < 0) || (toIndex > array.length) ||
+ (fromIndex > toIndex)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ return new COWSubList(fromIndex, toIndex - fromIndex);
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+
+ Object[] array = getArray();
+ int length = array.length;
+ out.writeInt(length);
+
+ for (int i = 0; i < length; i++)
+ out.writeObject(array[i]);
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ int length = in.readInt();
+ Object[] array = new Object[length];
+
+ for (int i = 0; i < length; i++) {
+ array[i] = in.readObject();
+ }
+
+ setArray(array);
+ }
+
+ public String toString() {
+ Object[] array = getArray();
+ int length = array.length;
+ StringBuffer buf = new StringBuffer();
+ buf.append('[');
+
+ for (int i = 0; i < length; i++) {
+ if (i > 0) {
+ buf.append(", ");
+ }
+
+ buf.append(array[i]);
+ }
+
+ buf.append(']');
+
+ return buf.toString();
+ }
+
+ private static boolean eq(Object o1, Object o2) {
+ return ((o1 == null) ? (o2 == null) : o1.equals(o2));
+ }
+
+ static class COWIterator implements ListIterator {
+ final Object[] array;
+ int cursor;
+
+ COWIterator(Object[] array, int cursor) {
+ this.array = array;
+ this.cursor = cursor;
+ }
+
+ public boolean hasNext() {
+ return cursor < array.length;
+ }
+
+ public boolean hasPrevious() {
+ return cursor > 0;
+ }
+
+ public int nextIndex() {
+ return cursor;
+ }
+
+ public Object next() {
+ try {
+ return array[cursor++];
+ } catch (IndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
+
+ // todo: should decrement cursor on failure?...
+ }
+
+ public int previousIndex() {
+ return cursor - 1;
+ }
+
+ public Object previous() {
+ try {
+ return array[--cursor];
+ } catch (IndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
+
+ // todo: should decrement cursor on failure?...
+ }
+
+ public void add(Object val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void set(Object val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ class COWSubList implements Serializable, List {
+ final int offset;
+ int length;
+ Object[] expectedArray;
+
+ COWSubList(int offset, int length) {
+ this.offset = offset;
+ this.length = length;
+ this.expectedArray = getArray();
+ }
+
+ public int size() {
+ return length;
+ }
+
+ public boolean isEmpty() {
+ return length == 0;
+ }
+
+ public boolean contains(Object o) {
+ return search(getArray(), o, offset, offset + length) >= 0;
+ }
+
+ public Iterator iterator() {
+ return listIterator();
+ }
+
+ public Object[] toArray() {
+ Object[] array = getArray();
+ Object[] newarr = new Object[length];
+ System.arraycopy(array, offset, newarr, 0, length);
+
+ return newarr;
+ }
+
+ public Object[] toArray(Object[] a) {
+ Object[] array = getArray();
+
+ if (a.length < length) {
+ a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+ length);
+ System.arraycopy(array, offset, a, 0, length);
+ } else {
+ System.arraycopy(array, offset, a, 0, length);
+
+ if (a.length > length) {
+ a[length] = null;
+ }
+ }
+
+ return a;
+ }
+
+ public boolean add(Object o) {
+ add(length, o);
+
+ return true;
+ }
+
+ public boolean remove(Object o) {
+ synchronized (CopyOnWriteArrayList.this) {
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = array.length;
+ int pos = search(array, o, offset, length);
+
+ if (pos < 0) {
+ return false;
+ }
+
+ Object[] newarr = new Object[fullLength - 1];
+ int moved = length - pos - 1;
+
+ if (pos > 0) {
+ System.arraycopy(array, 0, newarr, 0, pos);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, pos + 1, newarr, pos, moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length--;
+
+ return true;
+ }
+ }
+
+ public boolean containsAll(Collection c) {
+ Object[] array = getArray();
+
+ for (Iterator itr = c.iterator(); itr.hasNext();) {
+ if (search(array, itr.next(), offset, length) < 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean addAll(Collection c) {
+ return addAll(length, c);
+ }
+
+ public boolean addAll(int index, Collection c) {
+ int added = c.size();
+
+ synchronized (CopyOnWriteArrayList.this) {
+ if ((index < 0) || (index >= length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] oldarr = getArray();
+
+ if (oldarr != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ if (added == 0) {
+ return false;
+ }
+
+ int fullLength = oldarr.length;
+ Object[] newarr = new Object[fullLength + added];
+ int pos = offset + index;
+ int newpos = pos;
+ System.arraycopy(oldarr, 0, newarr, 0, pos);
+
+ int rem = fullLength - pos;
+
+ for (Iterator itr = c.iterator(); itr.hasNext();) {
+ newarr[newpos++] = itr.next();
+ }
+
+ if (rem > 0) {
+ System.arraycopy(oldarr, pos, newarr, newpos, rem);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length += added;
+
+ return true;
+ }
+ }
+
+ public boolean removeAll(Collection c) {
+ if (c.isEmpty()) {
+ return false;
+ }
+
+ synchronized (CopyOnWriteArrayList.this) {
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = array.length;
+ Object[] tmp = new Object[length];
+ int retained = 0;
+
+ for (int i = offset; i < (offset + length); i++) {
+ Object o = array[i];
+
+ if (!c.contains(o)) {
+ tmp[retained++] = o;
+ }
+ }
+
+ if (retained == length) {
+ return false;
+ }
+
+ Object[] newarr = new Object[(fullLength + retained) - length];
+ int moved = fullLength - offset - length;
+
+ if (offset > 0) {
+ System.arraycopy(array, 0, newarr, 0, offset);
+ }
+
+ if (retained > 0) {
+ System.arraycopy(tmp, 0, newarr, offset, retained);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, offset + length, newarr,
+ offset + retained, moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length = retained;
+
+ return true;
+ }
+ }
+
+ public boolean retainAll(Collection c) {
+ synchronized (CopyOnWriteArrayList.this) {
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = array.length;
+ Object[] tmp = new Object[length];
+ int retained = 0;
+
+ for (int i = offset; i < (offset + length); i++) {
+ Object o = array[i];
+
+ if (c.contains(o)) {
+ tmp[retained++] = o;
+ }
+ }
+
+ if (retained == length) {
+ return false;
+ }
+
+ Object[] newarr = new Object[(fullLength + retained) - length];
+ int moved = fullLength - offset - length;
+
+ if (offset > 0) {
+ System.arraycopy(array, 0, newarr, 0, offset);
+ }
+
+ if (retained > 0) {
+ System.arraycopy(tmp, 0, newarr, offset, retained);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, offset + length, newarr,
+ offset + retained, moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length = retained;
+
+ return true;
+ }
+ }
+
+ public void clear() {
+ synchronized (CopyOnWriteArrayList.this) {
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = array.length;
+ Object[] newarr = new Object[fullLength - length];
+ int moved = fullLength - offset - length;
+
+ if (offset > 0) {
+ System.arraycopy(array, 0, newarr, 0, offset);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, offset + length, newarr, offset,
+ moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length = 0;
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof List)) {
+ return false;
+ }
+
+ Object[] array;
+ int last;
+
+ synchronized (CopyOnWriteArrayList.this) {
+ array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ last = offset + length;
+ }
+
+ ListIterator itr = ((List) o).listIterator();
+ int idx = offset;
+
+ while ((idx < last) && itr.hasNext()) {
+ Object o1 = array[idx];
+ Object o2 = itr.next();
+
+ if (!eq(o1, o2)) {
+ return false;
+ }
+ }
+
+ return ((idx == last) && !itr.hasNext());
+ }
+
+ public int hashCode() {
+ int hashCode = 1;
+ Object[] array;
+ int last;
+
+ synchronized (CopyOnWriteArrayList.this) {
+ array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ last = offset + length;
+ }
+
+ for (int i = offset; i < last; i++) {
+ Object o = array[i];
+ hashCode = (31 * hashCode) + ((o == null) ? 0 : o.hashCode());
+ }
+
+ return hashCode;
+ }
+
+ public Object get(int index) {
+ return getArray()[offset + index];
+ }
+
+ public Object set(int index, Object element) {
+ synchronized (CopyOnWriteArrayList.this) {
+ if ((index < 0) || (index >= length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] oldarr = getArray();
+
+ if (oldarr != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = oldarr.length;
+
+ // piggyback the array bounds check
+ Object oldVal = oldarr[offset + index];
+
+ if (oldVal == element) {
+ setArray(oldarr);
+ } else {
+ Object[] newarr = new Object[fullLength];
+ System.arraycopy(oldarr, 0, newarr, 0, fullLength);
+ newarr[offset + index] = element;
+ setArray(newarr);
+ expectedArray = newarr;
+ }
+
+ return oldVal;
+ }
+ }
+
+ public void add(int index, Object element) {
+ synchronized (CopyOnWriteArrayList.this) {
+ if ((index < 0) || (index > length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] oldarr = getArray();
+
+ if (oldarr != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = oldarr.length;
+ Object[] newarr = new Object[fullLength + 1];
+ int pos = offset + index;
+ int moved = fullLength - pos;
+ System.arraycopy(oldarr, 0, newarr, 0, pos);
+ newarr[pos] = element;
+
+ if (moved > 0) {
+ System.arraycopy(oldarr, pos, newarr, pos + 1, moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length++;
+ }
+ }
+
+ public Object remove(int index) {
+ synchronized (CopyOnWriteArrayList.this) {
+ if ((index < 0) || (index >= length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ int fullLength = array.length;
+ int pos = offset + index;
+ Object result = array[pos];
+ Object[] newarr = new Object[fullLength - 1];
+ int moved = fullLength - pos - 1;
+
+ if (index > 0) {
+ System.arraycopy(array, 0, newarr, 0, pos);
+ }
+
+ if (moved > 0) {
+ System.arraycopy(array, pos + 1, newarr, pos, moved);
+ }
+
+ setArray(newarr);
+ expectedArray = newarr;
+ length--;
+
+ return result;
+ }
+ }
+
+ public int indexOf(Object o) {
+ int pos = search(getArray(), o, offset, offset + length);
+
+ return (pos >= 0) ? (pos - offset) : (-1);
+ }
+
+ public int indexOf(Object o, int index) {
+ int pos = search(getArray(), o, offset + index, offset + length) -
+ offset;
+
+ return (pos >= 0) ? (pos - offset) : (-1);
+ }
+
+ public int lastIndexOf(Object o) {
+ int pos = reverseSearch(getArray(), o, offset, offset + length) -
+ offset;
+
+ return (pos >= 0) ? (pos - offset) : (-1);
+ }
+
+ public int lastIndexOf(Object o, int index) {
+ int pos = reverseSearch(getArray(), o, offset, offset + index) -
+ offset;
+
+ return (pos >= 0) ? (pos - offset) : (-1);
+ }
+
+ public ListIterator listIterator() {
+ // must synchronize to atomically obtain the array and length
+ synchronized (CopyOnWriteArrayList.this) {
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ return new COWSubIterator(array, offset, offset + length, offset);
+ }
+ }
+
+ public ListIterator listIterator(int index) {
+ // must synchronize to atomically obtain the array and length
+ synchronized (CopyOnWriteArrayList.this) {
+ if ((index < 0) || (index >= length)) {
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size: " + length);
+ }
+
+ Object[] array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ return new COWSubIterator(array, offset, offset + length,
+ offset + index);
+ }
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ if ((fromIndex < 0) || (toIndex > length) || (fromIndex > toIndex)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ return new COWSubList(offset + fromIndex, toIndex - fromIndex);
+ }
+
+ public String toString() {
+ Object[] array;
+ int last;
+
+ synchronized (CopyOnWriteArrayList.this) {
+ array = getArray();
+
+ if (array != expectedArray) {
+ throw new ConcurrentModificationException();
+ }
+
+ last = offset + length;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ buf.append('[');
+
+ for (int i = offset; i < last; i++) {
+ if (i > offset) {
+ buf.append(", ");
+ }
+
+ buf.append(array[i]);
+ }
+
+ buf.append(']');
+
+ return buf.toString();
+ }
+ }
+
+ static class COWSubIterator implements ListIterator {
+ final Object[] array;
+ int cursor;
+ int first;
+ int last;
+
+ COWSubIterator(Object[] array, int first, int last, int cursor) {
+ this.array = array;
+ this.first = first;
+ this.last = last;
+ this.cursor = cursor;
+ }
+
+ public boolean hasNext() {
+ return cursor < last;
+ }
+
+ public boolean hasPrevious() {
+ return cursor > first;
+ }
+
+ public int nextIndex() {
+ return cursor - first;
+ }
+
+ public Object next() {
+ if (cursor == last) {
+ throw new NoSuchElementException();
+ }
+
+ return array[cursor++];
+ }
+
+ public int previousIndex() {
+ return cursor - first - 1;
+ }
+
+ public Object previous() {
+ if (cursor == first) {
+ throw new NoSuchElementException();
+ }
+
+ return array[--cursor];
+ }
+
+ public void add(Object val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void set(Object val) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArrayList.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,390 @@
+/*
+ * 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. Use, modify, and
+ * redistribute this code in any way without acknowledgement.
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+import java.util.*;
+
+
+/**
+ * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList}
+ * for all of its operations. Thus, it shares the same basic properties:
+ * <ul>
+ * <li>It is best suited for applications in which set sizes generally
+ * stay small, read-only operations
+ * vastly outnumber mutative operations, and you need
+ * to prevent interference among threads during traversal.
+ * <li>It is thread-safe.
+ * <li>Mutative operations (<tt>add</tt>, <tt>set</tt>, <tt>remove</tt>, etc.)
+ * are expensive since they usually entail copying the entire underlying
+ * array.
+ * <li>Iterators do not support the mutative <tt>remove</tt> operation.
+ * <li>Traversal via iterators is fast and cannot encounter
+ * interference from other threads. Iterators rely on
+ * unchanging snapshots of the array at the time the iterators were
+ * constructed.
+ * </ul>
+ *
+ * <p> <b>Sample Usage.</b> The following code sketch uses a
+ * copy-on-write set to maintain a set of Handler objects that
+ * perform some action upon state updates.
+ *
+ * <pre>
+ * class Handler { void handle(); ... }
+ *
+ * class X {
+ * private final CopyOnWriteArraySet<Handler> handlers
+ * = new CopyOnWriteArraySet<Handler>();
+ * public void addHandler(Handler h) { handlers.add(h); }
+ *
+ * private long internalState;
+ * private synchronized void changeState() { internalState = ...; }
+ *
+ * public void update() {
+ * changeState();
+ * for (Handler handler : handlers)
+ * handler.handle();
+ * }
+ * }
+ * </pre>
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/../guide/collections/index.html">
+ * Java Collections Framework</a>.
+ *
+ * @see CopyOnWriteArrayList
+ * @since 1.5
+ * @author Doug Lea
+ */
+public class CopyOnWriteArraySet extends AbstractSet implements java.io.Serializable {
+ private static final long serialVersionUID = 5457747651344034263L;
+ private final CopyOnWriteArrayList al;
+
+ /**
+ * Creates an empty set.
+ */
+ public CopyOnWriteArraySet() {
+ al = new CopyOnWriteArrayList();
+ }
+
+ /**
+ * Creates a set containing all of the elements of the specified
+ * collection.
+ *
+ * @param c the collection of elements to initially contain
+ * @throws NullPointerException if the specified collection is null
+ */
+ public CopyOnWriteArraySet(Collection c) {
+ al = new CopyOnWriteArrayList();
+ al.addAllAbsent(c);
+ }
+
+ /**
+ * Returns the number of elements in this set.
+ *
+ * @return the number of elements in this set
+ */
+ public int size() {
+ return al.size();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains no elements.
+ *
+ * @return <tt>true</tt> if this set contains no elements
+ */
+ public boolean isEmpty() {
+ return al.isEmpty();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains the specified element.
+ * More formally, returns <tt>true</tt> if and only if this set
+ * contains an element <tt>e</tt> such that
+ * <tt>(o==null ? e==null : o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this set is to be tested
+ * @return <tt>true</tt> if this set contains the specified element
+ */
+ public boolean contains(Object o) {
+ return al.contains(o);
+ }
+
+ /**
+ * Returns an array containing all of the elements in this set.
+ * If this set makes any guarantees as to what order its elements
+ * are returned by its iterator, this method must return the
+ * elements in the same order.
+ *
+ * <p>The returned array will be "safe" in that no references to it
+ * are maintained by this set. (In other words, this method must
+ * allocate a new array even if this set is backed by an array).
+ * The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all the elements in this set
+ */
+ public Object[] toArray() {
+ return al.toArray();
+ }
+
+ /**
+ * Returns an array containing all of the elements in this set; the
+ * runtime type of the returned array is that of the specified array.
+ * If the set fits in the specified array, it is returned therein.
+ * Otherwise, a new array is allocated with the runtime type of the
+ * specified array and the size of this set.
+ *
+ * <p>If this set fits in the specified array with room to spare
+ * (i.e., the array has more elements than this set), the element in
+ * the array immediately following the end of the set is set to
+ * <tt>null</tt>. (This is useful in determining the length of this
+ * set <i>only</i> if the caller knows that this set does not contain
+ * any null elements.)
+ *
+ * <p>If this set makes any guarantees as to what order its elements
+ * are returned by its iterator, this method must return the elements
+ * in the same order.
+ *
+ * <p>Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * <p>Suppose <tt>x</tt> is a set known to contain only strings.
+ * The following code can be used to dump the set into a newly allocated
+ * array of <tt>String</tt>:
+ *
+ * <pre>
+ * String[] y = x.toArray(new String[0]);</pre>
+ *
+ * Note that <tt>toArray(new Object[0])</tt> is identical in function to
+ * <tt>toArray()</tt>.
+ *
+ * @param a the array into which the elements of this set are to be
+ * stored, if it is big enough; otherwise, a new array of the same
+ * runtime type is allocated for this purpose.
+ * @return an array containing all the elements in this set
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in this
+ * set
+ * @throws NullPointerException if the specified array is null
+ */
+ public Object[] toArray(Object[] a) {
+ return al.toArray(a);
+ }
+
+ /**
+ * Removes all of the elements from this set.
+ * The set will be empty after this call returns.
+ */
+ public void clear() {
+ al.clear();
+ }
+
+ /**
+ * Removes the specified element from this set if it is present.
+ * More formally, removes an element <tt>e</tt> such that
+ * <tt>(o==null ? e==null : o.equals(e))</tt>,
+ * if this set contains such an element. Returns <tt>true</tt> if
+ * this set contained the element (or equivalently, if this set
+ * changed as a result of the call). (This set will not contain the
+ * element once the call returns.)
+ *
+ * @param o object to be removed from this set, if present
+ * @return <tt>true</tt> if this set contained the specified element
+ */
+ public boolean remove(Object o) {
+ return al.remove(o);
+ }
+
+ /**
+ * Adds the specified element to this set if it is not already present.
+ * More formally, adds the specified element <tt>e</tt> to this set if
+ * the set contains no element <tt>e2</tt> such that
+ * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
+ * If this set already contains the element, the call leaves the set
+ * unchanged and returns <tt>false</tt>.
+ *
+ * @param e element to be added to this set
+ * @return <tt>true</tt> if this set did not already contain the specified
+ * element
+ */
+ public boolean add(Object e) {
+ return al.addIfAbsent(e);
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains all of the elements of the
+ * specified collection. If the specified collection is also a set, this
+ * method returns <tt>true</tt> if it is a <i>subset</i> of this set.
+ *
+ * @param c collection to be checked for containment in this set
+ * @return <tt>true</tt> if this set contains all of the elements of the
+ * specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @see #contains(Object)
+ */
+ public boolean containsAll(Collection c) {
+ return al.containsAll(c);
+ }
+
+ /**
+ * Adds all of the elements in the specified collection to this set if
+ * they're not already present. If the specified collection is also a
+ * set, the <tt>addAll</tt> operation effectively modifies this set so
+ * that its value is the <i>union</i> of the two sets. The behavior of
+ * this operation is undefined if the specified collection is modified
+ * while the operation is in progress.
+ *
+ * @param c collection containing elements to be added to this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(Object)
+ */
+ public boolean addAll(Collection c) {
+ return al.addAllAbsent(c) > 0;
+ }
+
+ /**
+ * Removes from this set all of its elements that are contained in the
+ * specified collection. If the specified collection is also a set,
+ * this operation effectively modifies this set so that its value is the
+ * <i>asymmetric set difference</i> of the two sets.
+ *
+ * @param c collection containing elements to be removed from this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws ClassCastException if the class of an element of this set
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this set contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
+ */
+ public boolean removeAll(Collection c) {
+ return al.removeAll(c);
+ }
+
+ /**
+ * Retains only the elements in this set that are contained in the
+ * specified collection. In other words, removes from this set all of
+ * its elements that are not contained in the specified collection. If
+ * the specified collection is also a set, this operation effectively
+ * modifies this set so that its value is the <i>intersection</i> of the
+ * two sets.
+ *
+ * @param c collection containing elements to be retained in this set
+ * @return <tt>true</tt> if this set changed as a result of the call
+ * @throws ClassCastException if the class of an element of this set
+ * is incompatible with the specified collection (optional)
+ * @throws NullPointerException if this set contains a null element and the
+ * specified collection does not permit null elements (optional),
+ * or if the specified collection is null
+ * @see #remove(Object)
+ */
+ public boolean retainAll(Collection c) {
+ return al.retainAll(c);
+ }
+
+ /**
+ * Returns an iterator over the elements contained in this set
+ * in the order in which these elements were added.
+ *
+ * <p>The returned iterator provides a snapshot of the state of the set
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * <tt>remove</tt> method.
+ *
+ * @return an iterator over the elements in this set
+ */
+ public Iterator iterator() {
+ return al.iterator();
+ }
+
+ /**
+ * Compares the specified object with this set for equality.
+ * Returns {@code true} if the specified object is the same object
+ * as this object, or if it is also a {@link Set} and the elements
+ * returned by an {@linkplain List#iterator() iterator} over the
+ * specified set are the same as the elements returned by an
+ * iterator over this set. More formally, the two iterators are
+ * considered to return the same elements if they return the same
+ * number of elements and for every element {@code e1} returned by
+ * the iterator over the specified set, there is an element
+ * {@code e2} returned by the iterator over this set such that
+ * {@code (e1==null ? e2==null : e1.equals(e2))}.
+ *
+ * @param o object to be compared for equality with this set
+ * @return {@code true} if the specified object is equal to this set
+ */
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof Set)) {
+ return false;
+ }
+
+ Set set = (Set) (o);
+ Iterator it = set.iterator();
+
+ // Uses O(n^2) algorithm that is only appropriate
+ // for small sets, which CopyOnWriteArraySets should be.
+
+ // Use a single snapshot of underlying array
+ Object[] elements = al.getArray();
+ int len = elements.length;
+
+ // Mark matched elements to avoid re-checking
+ boolean[] matched = new boolean[len];
+ int k = 0;
+outer:
+ while (it.hasNext()) {
+ if (++k > len) {
+ return false;
+ }
+
+ Object x = it.next();
+
+ for (int i = 0; i < len; ++i) {
+ if (!matched[i] && eq(x, elements[i])) {
+ matched[i] = true;
+
+ continue outer;
+ }
+ }
+
+ return false;
+ }
+
+ return k == len;
+ }
+
+ /**
+ * Test for equality, coping with nulls.
+ */
+ private static boolean eq(Object o1, Object o2) {
+ return ((o1 == null) ? (o2 == null) : o1.equals(o2));
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/CopyOnWriteArraySet.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,198 @@
+/*
+ * 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 FIFOCondVar extends CondVar implements Condition, java.io.Serializable {
+ private static final WaitQueue.QueuedSync sync = new WaitQueue.QueuedSync() {
+ public boolean recheck(WaitQueue.WaitNode node) {
+ return false;
+ }
+
+ public void takeOver(WaitQueue.WaitNode node) {
+ }
+ };
+
+ // wait queue; only accessed when holding the lock
+ private final WaitQueue wq = new FIFOWaitQueue();
+
+ /**
+ * Create a new CondVar that relies on the given mutual exclusion lock.
+ * @param lock A non-reentrant mutual exclusion lock.
+ */
+ FIFOCondVar(ExclusiveLock lock) {
+ super(lock);
+ }
+
+ public void awaitUninterruptibly() {
+ int holdCount = lock.getHoldCount();
+
+ if (holdCount == 0) {
+ throw new IllegalMonitorStateException();
+ }
+
+ WaitQueue.WaitNode n = new WaitQueue.WaitNode();
+ wq.insert(n);
+
+ for (int i = holdCount; i > 0; i--)
+ lock.unlock();
+
+ try {
+ n.doWaitUninterruptibly(sync);
+ } finally {
+ for (int i = holdCount; i > 0; i--)
+ lock.lock();
+ }
+ }
+
+ public void await() throws InterruptedException {
+ int holdCount = lock.getHoldCount();
+
+ if (holdCount == 0) {
+ throw new IllegalMonitorStateException();
+ }
+
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+
+ WaitQueue.WaitNode n = new WaitQueue.WaitNode();
+ wq.insert(n);
+
+ for (int i = holdCount; i > 0; i--)
+ lock.unlock();
+
+ try {
+ n.doWait(sync);
+ } 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);
+ WaitQueue.WaitNode n = new WaitQueue.WaitNode();
+ wq.insert(n);
+
+ boolean success = false;
+
+ for (int i = holdCount; i > 0; i--)
+ lock.unlock();
+
+ try {
+ success = n.doTimedWait(sync, nanos);
+ } 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();
+ }
+
+ long abstime = deadline.getTime();
+ long start = System.currentTimeMillis();
+ long msecs = abstime - start;
+
+ return await(msecs, TimeUnit.MILLISECONDS);
+ }
+
+ public void signal() {
+ if (!lock.isHeldByCurrentThread()) {
+ throw new IllegalMonitorStateException();
+ }
+
+ for (;;) {
+ WaitQueue.WaitNode w = wq.extract();
+
+ if (w == null) {
+ return; // no one to signal
+ }
+
+ if (w.signal(sync)) {
+ return; // notify if still waiting, else skip
+ }
+ }
+ }
+
+ public void signalAll() {
+ if (!lock.isHeldByCurrentThread()) {
+ throw new IllegalMonitorStateException();
+ }
+
+ for (;;) {
+ WaitQueue.WaitNode w = wq.extract();
+
+ if (w == null) {
+ return; // no more to signal
+ }
+
+ w.signal(sync);
+ }
+ }
+
+ protected boolean hasWaiters() {
+ if (!lock.isHeldByCurrentThread()) {
+ throw new IllegalMonitorStateException();
+ }
+
+ return wq.hasNodes();
+ }
+
+ protected int getWaitQueueLength() {
+ if (!lock.isHeldByCurrentThread()) {
+ throw new IllegalMonitorStateException();
+ }
+
+ return wq.getLength();
+ }
+
+ protected Collection getWaitingThreads() {
+ if (!lock.isHeldByCurrentThread()) {
+ throw new IllegalMonitorStateException();
+ }
+
+ return wq.getWaitingThreads();
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOCondVar.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,114 @@
+/*
+ * 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.*;
+
+
+/**
+ * Simple linked list queue used in FIFOSemaphore.
+ * Methods are not synchronized; they depend on synch of callers.
+ * Must be public, since it is used by Semaphore (outside this package).
+ * NOTE: this class is NOT present in java.util.concurrent.
+ */
+class FIFOWaitQueue extends WaitQueue implements java.io.Serializable {
+ protected transient WaitNode head_ = null;
+ protected transient WaitNode tail_ = null;
+
+ public FIFOWaitQueue() {
+ }
+
+ public void insert(WaitNode w) {
+ if (tail_ == null) {
+ head_ = tail_ = w;
+ } else {
+ tail_.next = w;
+ tail_ = w;
+ }
+ }
+
+ public WaitNode extract() {
+ if (head_ == null) {
+ return null;
+ } else {
+ WaitNode w = head_;
+ head_ = w.next;
+
+ if (head_ == null) {
+ tail_ = null;
+ }
+
+ w.next = null;
+
+ return w;
+ }
+ }
+
+ public boolean hasNodes() {
+ return head_ != null;
+ }
+
+ public int getLength() {
+ int count = 0;
+ WaitNode node = head_;
+
+ while (node != null) {
+ if (node.waiting) {
+ count++;
+ }
+
+ node = node.next;
+ }
+
+ return count;
+ }
+
+ public Collection getWaitingThreads() {
+ List list = new ArrayList();
+ int count = 0;
+ WaitNode node = head_;
+
+ while (node != null) {
+ if (node.waiting) {
+ list.add(node.owner);
+ }
+
+ node = node.next;
+ }
+
+ return list;
+ }
+
+ public boolean isWaiting(Thread thread) {
+ if (thread == null) {
+ throw new NullPointerException();
+ }
+
+ for (WaitNode node = head_; node != null; node = node.next) {
+ if (node.waiting && (node.owner == thread)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/FIFOWaitQueue.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,328 @@
+/*
+ * 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;
+
+
+/**
+ * <tt>Lock</tt> implementations provide more extensive locking
+ * operations than can be obtained using <tt>synchronized</tt> methods
+ * and statements. They allow more flexible structuring, may have
+ * quite different properties, and may support multiple associated
+ * {@link Condition} objects.
+ *
+ * <p>A lock is a tool for controlling access to a shared resource by
+ * multiple threads. Commonly, a lock provides exclusive access to a
+ * shared resource: only one thread at a time can acquire the lock and
+ * all access to the shared resource requires that the lock be
+ * acquired first. However, some locks may allow concurrent access to
+ * a shared resource, such as the read lock of a {@link
+ * ReadWriteLock}.
+ *
+ * <p>The use of <tt>synchronized</tt> methods or statements provides
+ * access to the implicit monitor lock associated with every object, but
+ * forces all lock acquisition and release to occur in a block-structured way:
+ * when multiple locks are acquired they must be released in the opposite
+ * order, and all locks must be released in the same lexical scope in which
+ * they were acquired.
+ *
+ * <p>While the scoping mechanism for <tt>synchronized</tt> methods
+ * and statements makes it much easier to program with monitor locks,
+ * and helps avoid many common programming errors involving locks,
+ * there are occasions where you need to work with locks in a more
+ * flexible way. For example, some algorithms for traversing
+ * concurrently accessed data structures require the use of
+ * "hand-over-hand" or "chain locking": you
+ * acquire the lock of node A, then node B, then release A and acquire
+ * C, then release B and acquire D and so on. Implementations of the
+ * <tt>Lock</tt> interface enable the use of such techniques by
+ * allowing a lock to be acquired and released in different scopes,
+ * and allowing multiple locks to be acquired and released in any
+ * order.
+ *
+ * <p>With this increased flexibility comes additional
+ * responsibility. The absence of block-structured locking removes the
+ * automatic release of locks that occurs with <tt>synchronized</tt>
+ * methods and statements. In most cases, the following idiom
+ * should be used:
+ *
+ * <pre><tt> Lock l = ...;
+ * l.lock();
+ * try {
+ * // access the resource protected by this lock
+ * } finally {
+ * l.unlock();
+ * }
+ * </tt></pre>
+ *
+ * When locking and unlocking occur in different scopes, care must be
+ * taken to ensure that all code that is executed while the lock is
+ * held is protected by try-finally or try-catch to ensure that the
+ * lock is released when necessary.
+ *
+ * <p><tt>Lock</tt> implementations provide additional functionality
+ * over the use of <tt>synchronized</tt> methods and statements by
+ * providing a non-blocking attempt to acquire a lock ({@link
+ * #tryLock()}), an attempt to acquire the lock that can be
+ * interrupted ({@link #lockInterruptibly}, and an attempt to acquire
+ * the lock that can timeout ({@link #tryLock(long, TimeUnit)}).
+ *
+ * <p>A <tt>Lock</tt> class can also provide behavior and semantics
+ * that is quite different from that of the implicit monitor lock,
+ * such as guaranteed ordering, non-reentrant usage, or deadlock
+ * detection. If an implementation provides such specialized semantics
+ * then the implementation must document those semantics.
+ *
+ * <p>Note that <tt>Lock</tt> instances are just normal objects and can
+ * themselves be used as the target in a <tt>synchronized</tt> statement.
+ * Acquiring the
+ * monitor lock of a <tt>Lock</tt> instance has no specified relationship
+ * with invoking any of the {@link #lock} methods of that instance.
+ * It is recommended that to avoid confusion you never use <tt>Lock</tt>
+ * instances in this way, except 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>Memory Synchronization</h3>
+ * <p>All <tt>Lock</tt> implementations <em>must</em> enforce the same
+ * memory synchronization semantics as provided by the built-in monitor
+ * lock, as described in <a href="http://java.sun.com/docs/books/jls/">
+ * The Java Language Specification, Third Edition (17.4 Memory Model)</a>:
+ * <ul>
+ * <li>A successful <tt>lock</tt> operation has the same memory
+ * synchronization effects as a successful <em>Lock</em> action.
+ * <li>A successful <tt>unlock</tt> operation has the same
+ * memory synchronization effects as a successful <em>Unlock</em> action.
+ * </ul>
+ *
+ * Unsuccessful locking and unlocking operations, and reentrant
+ * locking/unlocking operations, do not require any memory
+ * synchronization effects.
+ *
+ * <h3>Implementation Considerations</h3>
+ *
+ * <p> The three forms of lock acquisition (interruptible,
+ * non-interruptible, and timed) may differ in their performance
+ * characteristics, ordering guarantees, or other implementation
+ * qualities. Further, the ability to interrupt the <em>ongoing</em>
+ * acquisition of a lock may not be available in a given <tt>Lock</tt>
+ * class. Consequently, an implementation is not required to define
+ * exactly the same guarantees or semantics for all three forms of
+ * lock acquisition, nor is it required to support interruption of an
+ * ongoing lock acquisition. An implementation is required to clearly
+ * document the semantics and guarantees provided by each of the
+ * locking methods. It must also obey the interruption semantics as
+ * defined in this interface, to the extent that interruption of lock
+ * acquisition is supported: which is either totally, or only on
+ * method entry.
+ *
+ * <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.
+ *
+ *
+ * @see ReentrantLock
+ * @see Condition
+ * @see ReadWriteLock
+ *
+ * @since 1.5
+ * @author Doug Lea
+ *
+ */
+public interface Lock {
+ /**
+ * Acquires the lock.
+ * <p>If the lock is not available then
+ * the current thread becomes disabled for thread scheduling
+ * purposes and lies dormant until the lock has been acquired.
+ * <p><b>Implementation Considerations</b>
+ * <p>A <tt>Lock</tt> implementation may be able to detect
+ * erroneous use of the lock, such as an invocation that would cause
+ * deadlock, and may throw an (unchecked) exception in such circumstances.
+ * The circumstances and the exception type must be documented by that
+ * <tt>Lock</tt> implementation.
+ */
+ void lock();
+
+ /**
+ * Acquires the lock unless the current thread is
+ * {@link Thread#interrupt interrupted}.
+ * <p>Acquires the lock if it is available and returns immediately.
+ * <p>If the lock is not available then
+ * the current thread becomes disabled for thread scheduling
+ * purposes and lies dormant until one of two things happens:
+ * <ul>
+ * <li>The lock is acquired by the current thread; or
+ * <li>Some other thread {@link Thread#interrupt interrupts} the current
+ * thread, and interruption of lock acquisition is supported.
+ * </ul>
+ * <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 acquiring
+ * the lock, and interruption of lock acquisition is supported,
+ * </ul>
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
+ *
+ * <p><b>Implementation Considerations</b>
+ *
+ * <p>The ability to interrupt a lock acquisition in some
+ * implementations may not be possible, and if possible may be an
+ * expensive operation. The programmer should be aware that this
+ * may be the case. An implementation should document when this is
+ * the case.
+ *
+ * <p>An implementation can favor responding to an interrupt over
+ * normal method return.
+ *
+ * <p>A <tt>Lock</tt> implementation may be able to detect
+ * erroneous use of the lock, such as an invocation that would
+ * cause deadlock, and may throw an (unchecked) exception in such
+ * circumstances. The circumstances and the exception type must
+ * be documented by that <tt>Lock</tt> implementation.
+ *
+ * @throws InterruptedException if the current thread is interrupted
+ * while acquiring the lock (and interruption of lock acquisition is
+ * supported).
+ *
+ * @see Thread#interrupt
+ */
+ void lockInterruptibly() throws InterruptedException;
+
+ /**
+ * Acquires the lock only if it is free at the time of invocation.
+ * <p>Acquires the lock if it is available and returns immediately
+ * with the value <tt>true</tt>.
+ * If the lock is not available then this method will return
+ * immediately with the value <tt>false</tt>.
+ * <p>A typical usage idiom for this method would be:
+ * <pre>
+ * Lock lock = ...;
+ * if (lock.tryLock()) {
+ * try {
+ * // manipulate protected state
+ * } finally {
+ * lock.unlock();
+ * }
+ * } else {
+ * // perform alternative actions
+ * }
+ * </pre>
+ * This usage ensures that the lock is unlocked if it was acquired, and
+ * doesn't try to unlock if the lock was not acquired.
+ *
+ * @return <tt>true</tt> if the lock was acquired and <tt>false</tt>
+ * otherwise.
+ */
+ boolean tryLock();
+
+ /**
+ * Acquires the lock if it is free within the given waiting time and the
+ * current thread has not been {@link Thread#interrupt interrupted}.
+ *
+ * <p>If the lock is available this method returns immediately
+ * with the value <tt>true</tt>.
+ * If the lock is not available then
+ * the current thread becomes disabled for thread scheduling
+ * purposes and lies dormant until one of three things happens:
+ * <ul>
+ * <li>The lock is acquired by the current thread; or
+ * <li>Some other thread {@link Thread#interrupt interrupts} the current
+ * thread, and interruption of lock acquisition is supported; or
+ * <li>The specified waiting time elapses
+ * </ul>
+ * <p>If the lock is acquired then the value <tt>true</tt> is returned.
+ * <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 acquiring
+ * the lock, and interruption of lock acquisition is supported,
+ * </ul>
+ * then {@link InterruptedException} is thrown and the current thread's
+ * interrupted status is cleared.
+ * <p>If the specified waiting time elapses then the value <tt>false</tt>
+ * is returned.
+ * If the time is
+ * less than or equal to zero, the method will not wait at all.
+ *
+ * <p><b>Implementation Considerations</b>
+ * <p>The ability to interrupt a lock acquisition in some implementations
+ * may not be possible, and if possible may
+ * be an expensive operation.
+ * The programmer should be aware that this may be the case. An
+ * implementation should document when this is the case.
+ * <p>An implementation can favor responding to an interrupt over normal
+ * method return, or reporting a timeout.
+ * <p>A <tt>Lock</tt> implementation may be able to detect
+ * erroneous use of the lock, such as an invocation that would cause
+ * deadlock, and may throw an (unchecked) exception in such circumstances.
+ * The circumstances and the exception type must be documented by that
+ * <tt>Lock</tt> implementation.
+ *
+ * @param time the maximum time to wait for the lock
+ * @param unit the time unit of the <tt>time</tt> argument.
+ * @return <tt>true</tt> if the lock was acquired and <tt>false</tt>
+ * if the waiting time elapsed before the lock was acquired.
+ *
+ * @throws InterruptedException if the current thread is interrupted
+ * while acquiring the lock (and interruption of lock acquisition is
+ * supported).
+ *
+ * @see Thread#interrupt
+ */
+ boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
+
+ /**
+ * Releases the lock.
+ * <p><b>Implementation Considerations</b>
+ * <p>A <tt>Lock</tt> implementation will usually impose
+ * restrictions on which thread can release a lock (typically only the
+ * holder of the lock can release it) and may throw
+ * an (unchecked) exception if the restriction is violated.
+ * Any restrictions and the exception
+ * type must be documented by that <tt>Lock</tt> implementation.
+ */
+ void unlock();
+
+ /**
+ * Returns a new {@link Condition} instance that is bound to this
+ * <tt>Lock</tt> instance.
+ * <p>Before waiting on the condition the lock must be held by the
+ * current thread.
+ * A call to {@link Condition#await()} will atomically release the lock
+ * before waiting and re-acquire the lock before the wait returns.
+ * <p><b>Implementation Considerations</b>
+ * <p>The exact operation of the {@link Condition} instance depends on the
+ * <tt>Lock</tt> implementation and must be documented by that
+ * implementation.
+ *
+ * @return A new {@link Condition} instance for this <tt>Lock</tt>
+ * instance.
+ * @throws UnsupportedOperationException if this <tt>Lock</tt>
+ * implementation does not support conditions.
+ */
+ Condition newCondition();
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/Lock.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java?rev=417856&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java Wed Jun 28 12:34:33 2006
@@ -0,0 +1,46 @@
+/*
+ * 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 Dawid Kurzyniec and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
+ */
+package org.apache.openjpa.lib.util.concurrent;
+
+
+/**
+ * Interface to specify custom implementation of precise timer.
+ *
+ * @author Dawid Kurzyniec
+ * @version 1.0
+ */
+public interface NanoTimer {
+ /**
+ * Returns the current value of the most precise available system timer,
+ * in nanoseconds. This method can only be used to measure elapsed time and
+ * is not related to any other notion of system or wall-clock time. The
+ * value returned represents nanoseconds since some fixed but arbitrary
+ * time (perhaps in the future, so values may be negative). This method
+ * provides nanosecond precision, but not necessarily nanosecond accuracy.
+ * No guarantees are made about how frequently values change. Differences
+ * in successive calls that span greater than approximately 292 years
+ * (263 nanoseconds) will not accurately compute elapsed time due to
+ * numerical overflow.
+ *
+ * @return The current value of the system timer, in nanoseconds.
+ */
+ long nanoTime();
+}
Propchange: incubator/openjpa/trunk/openjpa-lib/main/java/org/apache/openjpa/lib/util/concurrent/NanoTimer.java
------------------------------------------------------------------------------
svn:executable = *