implement ConcurrentHashMap and AtomicReferenceArray

This is the simplest possible ConcurrentHashMap I could come up with
that works and is actually concurrent in the way one would expect.
It's pretty unconventional, being based on a persistent red-black
tree, and not particularly memory-efficient or cache-friendly.  I
think this is a good place to start, though, and it should perform
reasonably well for most workloads.  Patches for a more efficient
implementation are welcome!

I also implemented AtomicReferenceArray, since I was using it in my
first, naive attempt to implement ConcurrentHashMap.

I had to do a bit of refactoring, including moving some non-standard
stuff from java.util.Collections to avian.Data so I could make it
available to code outside the java.util package, which is why I had to
modify several unrelated files.
This commit is contained in:
Joel Dice 2014-03-07 09:08:19 -07:00
parent b6c3bc6f4d
commit c0d178d5f1
19 changed files with 1074 additions and 284 deletions

310
classpath/avian/Data.java Normal file
View File

@ -0,0 +1,310 @@
/* Copyright (c) 2008-2014, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package avian;
import java.util.Map;
import java.util.Map.Entry;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Collections;
public class Data {
public static int nextPowerOfTwo(int n) {
int r = 1;
while (r < n) r <<= 1;
return r;
}
public static <V> boolean equal(V a, V b) {
return a == null ? b == null : a.equals(b);
}
public static <T> T[] toArray(Collection collection, T[] array) {
Class c = array.getClass().getComponentType();
if (array.length < collection.size()) {
array = (T[]) java.lang.reflect.Array.newInstance(c, collection.size());
}
int i = 0;
for (Object o: collection) {
if (c.isInstance(o)) {
array[i++] = (T) o;
} else {
throw new ArrayStoreException();
}
}
return array;
}
public static String toString(Collection c) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Iterator it = c.iterator(); it.hasNext();) {
sb.append(it.next());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
public static String toString(Map m) {
StringBuilder sb = new StringBuilder();
sb.append("{");
for (Iterator<Entry> it = m.entrySet().iterator(); it.hasNext();) {
Entry e = it.next();
sb.append(e.getKey())
.append("=")
.append(e.getValue());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append("}");
return sb.toString();
}
public interface EntryMap<K,V> {
public int size();
public Entry<K,V> find(Object key);
public Entry<K,V> remove(Object key);
public void clear();
public Iterator<Entry<K,V>> iterator();
}
public static class EntrySet<K, V> extends AbstractSet<Entry<K, V>> {
private final EntryMap<K, V> map;
public EntrySet(EntryMap<K, V> map) {
this.map = map;
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.size() == 0;
}
public boolean contains(Object o) {
return (o instanceof Entry<?,?>)
&& map.find(((Entry<?,?>)o).getKey()) != null;
}
public boolean add(Entry<K, V> e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
return (o instanceof Entry<?,?>)
&& map.remove(((Entry<?,?>) o).getKey()) != null;
}
public boolean remove(Entry<K, V> e) {
return map.remove(e.getKey()) != null;
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return toArray(this, array);
}
public void clear() {
map.clear();
}
public Iterator<Entry<K, V>> iterator() {
return map.iterator();
}
}
public static class KeySet<K> extends AbstractSet<K> {
private final EntryMap<K, ?> map;
public KeySet(EntryMap<K, ?> map) {
this.map = map;
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.size() == 0;
}
public boolean contains(Object key) {
return map.find(key) != null;
}
public boolean add(K key) {
throw new UnsupportedOperationException();
}
public boolean remove(Object key) {
return map.remove(key) != null;
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return toArray(this, array);
}
public void clear() {
map.clear();
}
public Iterator<K> iterator() {
return new KeyIterator(map.iterator());
}
}
public static class Values<K, V> implements Collection<V> {
private final EntryMap<K, V> map;
public Values(EntryMap<K, V> map) {
this.map = map;
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.size() == 0;
}
public boolean contains(Object value) {
for (Iterator<Entry<K, V>> it = map.iterator(); it.hasNext();) {
if (equal(it.next().getValue(), value)) {
return true;
}
}
return false;
}
public boolean containsAll(Collection<?> c) {
if (c == null) {
throw new NullPointerException("collection is null");
}
for (Iterator<?> it = c.iterator(); it.hasNext();) {
if (! contains(it.next())) {
return false;
}
}
return true;
}
public boolean add(V value) {
throw new UnsupportedOperationException();
}
public boolean addAll(Collection<? extends V> collection) {
throw new UnsupportedOperationException();
}
public boolean remove(Object value) {
for (Iterator<Entry<K, V>> it = map.iterator();
it.hasNext();)
{
if (equal(it.next().getValue(), value)) {
it.remove();
return true;
}
}
return false;
}
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Iterator<Entry<K, V>> it = map.iterator(); it.hasNext();) {
if (c.contains(it.next().getValue())) {
it.remove();
changed = true;
}
}
return changed;
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return toArray(this, array);
}
public void clear() {
map.clear();
}
public Iterator<V> iterator() {
return new ValueIterator(map.iterator());
}
}
public static class KeyIterator<K, V> implements Iterator<K> {
private final Iterator<Entry<K, V>> it;
public KeyIterator(Iterator<Entry<K, V>> it) {
this.it = it;
}
public K next() {
return it.next().getKey();
}
public boolean hasNext() {
return it.hasNext();
}
public void remove() {
it.remove();
}
}
public static class ValueIterator<K, V> implements Iterator<V> {
private final Iterator<Entry<K, V>> it;
public ValueIterator(Iterator<Entry<K, V>> it) {
this.it = it;
}
public V next() {
return it.next().getValue();
}
public boolean hasNext() {
return it.hasNext();
}
public void remove() {
it.remove();
}
}
}

View File

@ -97,12 +97,12 @@ public abstract class AbstractCollection<T> implements Collection<T> {
}
public <S> S[] toArray(S[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public abstract Iterator<T> iterator();
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
}

View File

@ -185,7 +185,7 @@ public class ArrayList<T> extends AbstractList<T> implements java.io.Serializabl
}
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
private void writeObject(ObjectOutputStream out) throws IOException {

View File

@ -10,12 +10,14 @@
package java.util;
import avian.Data;
public class Collections {
private Collections() { }
public static void shuffle(List list, Random random) {
Object[] array = toArray(list, new Object[list.size()]);
Object[] array = Data.toArray(list, new Object[list.size()]);
for (int i = 0; i < array.length; ++i) {
int j = random.nextInt(array.length);
Object tmp = array[i];
@ -173,25 +175,6 @@ public class Collections {
}
}
static <T> T[] toArray(Collection collection, T[] array) {
Class c = array.getClass().getComponentType();
if (array.length < collection.size()) {
array = (T[]) java.lang.reflect.Array.newInstance(c, collection.size());
}
int i = 0;
for (Object o: collection) {
if (c.isInstance(o)) {
array[i++] = (T) o;
} else {
throw new ArrayStoreException();
}
}
return array;
}
public static final List EMPTY_LIST
= new UnmodifiableList<Object>(new ArrayList<Object>(0));
@ -208,35 +191,6 @@ public class Collections {
return (Set<T>) new UnmodifiableSet<Object>(
new HashSet<Object>(0));
}
static String toString(Collection c) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Iterator it = c.iterator(); it.hasNext();) {
sb.append(it.next());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
static String toString(Map m) {
StringBuilder sb = new StringBuilder();
sb.append("{");
for (Iterator<Map.Entry> it = m.entrySet().iterator(); it.hasNext();) {
Map.Entry e = it.next();
sb.append(e.getKey())
.append("=")
.append(e.getValue());
if (it.hasNext()) {
sb.append(",");
}
}
sb.append("}");
return sb.toString();
}
public static <T> Enumeration<T> enumeration(Collection<T> c) {
return new IteratorEnumeration<T> (c.iterator());
@ -746,46 +700,6 @@ public class Collections {
public static <T> Set<T> unmodifiableSet(Set<T> hs) {
return new UnmodifiableSet<T>(hs);
}
static class KeyIterator<K, V> implements Iterator<K> {
private final Iterator<Map.Entry<K, V>> it;
public KeyIterator(Iterator<Map.Entry<K, V>> it) {
this.it = it;
}
public K next() {
return it.next().getKey();
}
public boolean hasNext() {
return it.hasNext();
}
public void remove() {
it.remove();
}
}
static class ValueIterator<K, V> implements Iterator<V> {
private final Iterator<Map.Entry<K, V>> it;
public ValueIterator(Iterator<Map.Entry<K, V>> it) {
this.it = it;
}
public V next() {
return it.next().getValue();
}
public boolean hasNext() {
return it.hasNext();
}
public void remove() {
it.remove();
}
}
private static final class ReverseComparator<T> implements Comparator<T> {

View File

@ -10,6 +10,8 @@
package java.util;
import avian.Data;
public class HashMap<K, V> implements Map<K, V> {
private static final int MinimumCapacity = 16;
@ -19,7 +21,7 @@ public class HashMap<K, V> implements Map<K, V> {
public HashMap(int capacity, Helper<K, V> helper) {
if (capacity > 0) {
array = new Cell[nextPowerOfTwo(capacity)];
array = new Cell[Data.nextPowerOfTwo(capacity)];
}
this.helper = helper;
}
@ -40,13 +42,7 @@ public class HashMap<K, V> implements Map<K, V> {
}
public String toString() {
return Collections.toString(this);
}
private static int nextPowerOfTwo(int n) {
int r = 1;
while (r < n) r <<= 1;
return r;
return avian.Data.toString(this);
}
public boolean isEmpty() {
@ -72,7 +68,7 @@ public class HashMap<K, V> implements Map<K, V> {
private void resize(int capacity) {
Cell<K, V>[] newArray = null;
if (capacity != 0) {
capacity = nextPowerOfTwo(capacity);
capacity = Data.nextPowerOfTwo(capacity);
if (array != null && array.length == capacity) {
return;
}
@ -220,21 +216,43 @@ public class HashMap<K, V> implements Map<K, V> {
}
public Set<Entry<K, V>> entrySet() {
return new EntrySet();
return new Data.EntrySet(new MyEntryMap());
}
public Set<K> keySet() {
return new KeySet();
return new Data.KeySet(new MyEntryMap());
}
public Collection<V> values() {
return new Values();
return new Data.Values(new MyEntryMap());
}
Iterator<Entry<K, V>> iterator() {
return new MyIterator();
}
private class MyEntryMap implements Data.EntryMap<K, V> {
public int size() {
return HashMap.this.size();
}
public Entry<K,V> find(Object key) {
return HashMap.this.find(key);
}
public Entry<K,V> remove(Object key) {
return removeCell(key);
}
public void clear() {
HashMap.this.clear();
}
public Iterator<Entry<K,V>> iterator() {
return HashMap.this.iterator();
}
}
interface Cell<K, V> extends Entry<K, V> {
public HashMap.Cell<K, V> next();
@ -303,148 +321,6 @@ public class HashMap<K, V> implements Map<K, V> {
}
}
private class EntrySet extends AbstractSet<Entry<K, V>> {
public int size() {
return HashMap.this.size();
}
public boolean isEmpty() {
return HashMap.this.isEmpty();
}
public boolean contains(Object o) {
return (o instanceof Entry<?,?>)
&& containsKey(((Entry<?,?>)o).getKey());
}
public boolean add(Entry<K, V> e) {
return putCell(e.getKey(), e.getValue()) != null;
}
public boolean remove(Object o) {
return (o instanceof Entry<?,?>) && remove((Entry<?,?>)o);
}
public boolean remove(Entry<K, V> e) {
return removeCell(e.getKey()) != null;
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
}
public void clear() {
HashMap.this.clear();
}
public Iterator<Entry<K, V>> iterator() {
return new MyIterator();
}
}
private class KeySet extends AbstractSet<K> {
public int size() {
return HashMap.this.size();
}
public boolean isEmpty() {
return HashMap.this.isEmpty();
}
public boolean contains(Object key) {
return containsKey(key);
}
public boolean add(K key) {
return putCell(key, null) != null;
}
public boolean remove(Object key) {
return removeCell(key) != null;
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
}
public void clear() {
HashMap.this.clear();
}
public Iterator<K> iterator() {
return new Collections.KeyIterator(new MyIterator());
}
}
private class Values implements Collection<V> {
public int size() {
return HashMap.this.size();
}
public boolean isEmpty() {
return HashMap.this.isEmpty();
}
public boolean contains(Object value) {
return containsValue(value);
}
public boolean containsAll(Collection<?> c) {
if (c == null) {
throw new NullPointerException("collection is null");
}
Iterator<?> it = c.iterator();
while (it.hasNext()) {
if (! contains(it.next())) {
return false;
}
}
return true;
}
public boolean add(V value) {
throw new UnsupportedOperationException();
}
public boolean addAll(Collection<? extends V> collection) {
throw new UnsupportedOperationException();
}
public boolean remove(Object value) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public Object[] toArray() {
return toArray(new Object[size()]);
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
}
public void clear() {
HashMap.this.clear();
}
public Iterator<V> iterator() {
return new Collections.ValueIterator(new MyIterator());
}
}
private class MyIterator implements Iterator<Entry<K, V>> {
private int currentIndex = -1;
private int nextIndex = -1;

View File

@ -63,7 +63,7 @@ public class HashSet<T> extends AbstractSet<T> implements Set<T> {
}
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
private static class MyIterator<T> implements Iterator<T> {

View File

@ -131,7 +131,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> {
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public void clear() {
@ -169,7 +169,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> {
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public void clear() {
@ -177,7 +177,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> {
}
public Iterator<K> iterator() {
return new Collections.KeyIterator(new MyIterator());
return new avian.Data.KeyIterator(new MyIterator());
}
}
@ -230,7 +230,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> {
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public void clear() {
@ -238,7 +238,7 @@ public class LinkedHashMap<K, V> extends HashMap<K, V> {
}
public Iterator<V> iterator() {
return new Collections.ValueIterator(new MyIterator());
return new avian.Data.ValueIterator(new MyIterator());
}
}

View File

@ -63,7 +63,7 @@ public class LinkedHashSet<T> extends AbstractSet<T> implements Set<T> {
}
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
private static class MyIterator<T> implements Iterator<T> {

View File

@ -360,7 +360,7 @@ public class LinkedList<T> extends AbstractSequentialList<T> implements Deque<T>
@Override
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
@Override

View File

@ -42,7 +42,7 @@ public class TreeMap<K,V> implements Map<K,V> {
}
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
public V get(Object key) {
@ -164,7 +164,7 @@ public class TreeMap<K,V> implements Map<K,V> {
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public void clear() {
@ -172,7 +172,7 @@ public class TreeMap<K,V> implements Map<K,V> {
}
public Iterator<K> iterator() {
return new Collections.KeyIterator(set.iterator());
return new avian.Data.KeyIterator(set.iterator());
}
}
@ -225,7 +225,7 @@ public class TreeMap<K,V> implements Map<K,V> {
}
public <T> T[] toArray(T[] array) {
return Collections.toArray(this, array);
return avian.Data.toArray(this, array);
}
public void clear() {
@ -233,7 +233,7 @@ public class TreeMap<K,V> implements Map<K,V> {
}
public Iterator<V> iterator() {
return new Collections.ValueIterator(set.iterator());
return new avian.Data.ValueIterator(set.iterator());
}
}

View File

@ -61,7 +61,7 @@ public class TreeSet<T> extends AbstractSet<T> implements Collection<T> {
}
public String toString() {
return Collections.toString(this);
return avian.Data.toString(this);
}
public boolean add(T value) {

View File

@ -0,0 +1,430 @@
/* Copyright (c) 2008-2014, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.util.concurrent;
import avian.Data;
import avian.PersistentSet;
import avian.PersistentSet.Path;
import sun.misc.Unsafe;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ConcurrentHashMap<K,V>
extends AbstractMap<K,V>
implements ConcurrentMap<K,V>
{
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long Content;
private static final Content Empty = new Content(new PersistentSet(), 0);
static {
try {
Content = unsafe.objectFieldOffset
(ConcurrentHashMap.class.getDeclaredField("content"));
} catch (NoSuchFieldException e) {
throw new Error(e);
}
}
private volatile Content<K,V> content;
public ConcurrentHashMap() {
content = Empty;
}
public ConcurrentHashMap(int initialCapacity) {
this();
}
public boolean isEmpty() {
return content.size == 0;
}
public int size() {
return content.size;
}
public boolean containsKey(Object key) {
return find(key) != null;
}
public boolean containsValue(Object value) {
for (V v: values()) {
if (value.equals(v)) {
return true;
}
}
return false;
}
public V get(Object key) {
Cell<K,V> cell = find(key);
return cell == null ? null : cell.value;
}
private Cell<K,V> find(Object key) {
Content<K,V> c = content;
Path<Node<Cell<K,V>>> path = c.set.find(new Node(key.hashCode()));
for (Cell<K,V> cell = path.value().value;
cell != null;
cell = cell.next)
{
if (key.equals(cell.key)) {
return cell;
}
}
return null;
}
public V putIfAbsent(K key, V value) {
Cell<K,V> cell = put(key, value, PutCondition.IfAbsent, null);
return cell == null ? null : cell.value;
}
public boolean remove(K key, V value) {
Cell<K,V> cell = remove(key, RemoveCondition.IfEqual, value);
return cell != null && cell.value.equals(value);
}
public V replace(K key, V value) {
Cell<K,V> cell = put(key, value, PutCondition.IfPresent, null);
return cell == null ? null : cell.value;
}
public boolean replace(K key, V oldValue, V newValue) {
Cell<K,V> cell = put(key, newValue, PutCondition.IfEqual, oldValue);
return cell != null && cell.value.equals(oldValue);
}
public V put(K key, V value) {
Cell<K,V> cell = put(key, value, PutCondition.Always, null);
return cell == null ? null : cell.value;
}
public V remove(Object key) {
Cell<K,V> cell = remove(key, RemoveCondition.Always, null);
return cell == null ? null : cell.value;
}
private enum PutCondition {
Always() {
public boolean addIfAbsent() { return true; }
public <V> boolean addIfPresent(V a, V b) { return true; }
}, IfAbsent() {
public boolean addIfAbsent() { return true; }
public <V> boolean addIfPresent(V a, V b) { return false; }
}, IfPresent() {
public boolean addIfAbsent() { return false; }
public <V> boolean addIfPresent(V a, V b) { return true; }
}, IfEqual() {
public boolean addIfAbsent() { return false; }
public <V> boolean addIfPresent(V a, V b) { return a.equals(b); }
};
public boolean addIfAbsent() { throw new AssertionError(); }
public <V> boolean addIfPresent(V a, V b) { throw new AssertionError(); }
}
private enum RemoveCondition {
Always() {
public <V> boolean remove(V a, V b) { return true; }
}, IfEqual() {
public <V> boolean remove(V a, V b) { return a.equals(b); }
};
public <V> boolean remove(V a, V b) { throw new AssertionError(); }
}
private Cell<K,V> put(K key, V value, PutCondition condition, V oldValue) {
Node<Cell<K,V>> node = new Node(key.hashCode());
loop: while (true) {
node.value = null;
Content content = this.content;
Path<Node<Cell<K,V>>> path = content.set.find(node);
for (Cell<K,V> cell = path.value().value;
cell != null;
cell = cell.next)
{
if (key.equals(cell.key)) {
if (! condition.addIfPresent(cell.value, oldValue)) {
return cell;
}
Cell<K,V> start = null;
Cell<K,V> last = null;
for (Cell<K,V> cell2 = path.value().value;
true;
cell2 = cell2.next)
{
Cell<K,V> c;
c = cell2.clone();
if (last == null) {
last = start = c;
} else {
last.next = c;
last = c;
}
if (cell2 == cell) {
c.value = value;
break;
}
}
node.value = start;
if (unsafe.compareAndSwapObject
(this, Content, content, new Content
(path.replaceWith(node), content.size)))
{
return cell;
} else {
continue loop;
}
}
}
// no mapping found -- add a new one if appropriate
if (! condition.addIfAbsent()) {
return null;
}
node.value = new Cell(key, value, null);
if (unsafe.compareAndSwapObject
(this, Content, content, new Content
(path.fresh() ? path.add() : path.replaceWith(node),
content.size + 1)))
{
return null;
}
}
}
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> e: map.entrySet()) {
put(e.getKey(), e.getValue());
}
}
private Cell<K,V> remove(Object key, RemoveCondition condition,
V oldValue)
{
Node<Cell<K,V>> node = new Node(key.hashCode());
loop: while (true) {
node.value = null;
Content content = this.content;
Path<Node<Cell<K,V>>> path = content.set.find(node);
for (Cell<K,V> cell = path.value().value;
cell != null;
cell = cell.next)
{
if (key.equals(cell.key)) {
if (! condition.remove(cell.value, oldValue)) {
return cell;
}
Cell<K,V> start = null;
Cell<K,V> last = null;
for (Cell<K,V> cell2 = path.value().value;
cell2 != cell;
cell2 = cell2.next)
{
Cell<K,V> c = cell2.clone();
if (last == null) {
last = start = c;
} else {
last.next = c;
last = c;
}
}
if (last == null) {
start = last = cell.next;
} else {
last.next = cell.next;
}
node.value = start;
if (unsafe.compareAndSwapObject
(this, Content, content, new Content
(start == null ? path.remove() : path.replaceWith(node),
content.size - 1)))
{
return cell;
} else {
continue loop;
}
}
}
return null;
}
}
public void clear() {
content = Empty;
}
public Set<Map.Entry<K, V>> entrySet() {
return new Data.EntrySet(new MyEntryMap());
}
public Set<K> keySet() {
return new Data.KeySet(new MyEntryMap());
}
public Collection<V> values() {
return new Data.Values(new MyEntryMap());
}
private class MyEntryMap implements Data.EntryMap<K, V> {
public int size() {
return ConcurrentHashMap.this.size();
}
public Map.Entry<K,V> find(Object key) {
return new MyEntry(ConcurrentHashMap.this.find(key));
}
public Map.Entry<K,V> remove(Object key) {
return new MyEntry
(ConcurrentHashMap.this.remove(key, RemoveCondition.Always, null));
}
public void clear() {
ConcurrentHashMap.this.clear();
}
public Iterator<Map.Entry<K,V>> iterator() {
return new MyIterator(content);
}
}
private static class Content<K,V> {
private final PersistentSet<Node<Cell<K,V>>> set;
private final int size;
public Content(PersistentSet<Node<Cell<K,V>>> set,
int size)
{
this.set = set;
this.size = size;
}
}
private static class Cell<K,V> implements Cloneable {
public final K key;
public V value;
public Cell<K,V> next;
public Cell(K key, V value, Cell<K,V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public Cell<K,V> clone() {
try {
return (Cell<K,V>) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
private static class Node<T> implements Comparable<Node<T>> {
public final int key;
public T value;
public Node(int key) {
this.key = key;
}
public int compareTo(Node<T> n) {
return key - n.key;
}
}
private class MyEntry implements Map.Entry<K,V> {
private final K key;
private V value;
public MyEntry(Cell<K,V> cell) {
key = cell.key;
value = cell.value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V v = value;
this.value = value;
put(key, value);
return v;
}
}
private class MyIterator implements Iterator<Map.Entry<K, V>> {
private final Content<K,V> content;
private final Iterator<Node<Cell<K, V>>> iterator;
private Cell<K, V> currentCell;
private Cell<K, V> nextCell;
public MyIterator(Content<K,V> content) {
this.content = content;
this.iterator = content.set.iterator();
hasNext();
}
public Map.Entry<K, V> next() {
if (hasNext()) {
currentCell = nextCell;
nextCell = nextCell.next;
return new MyEntry(currentCell);
} else {
throw new NoSuchElementException();
}
}
public boolean hasNext() {
if (nextCell == null && iterator.hasNext()) {
nextCell = iterator.next().value;
}
return nextCell != null;
}
public void remove() {
if (currentCell != null) {
ConcurrentHashMap.this.remove
(currentCell.key, RemoveCondition.Always, null);
currentCell = null;
} else {
throw new IllegalStateException();
}
}
}
}

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2008-2014, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.util.concurrent;
import java.util.Map;
public interface ConcurrentMap<K,V> extends Map<K,V> {
public V putIfAbsent(K key, V value);
public boolean remove(K key, V value);
public V replace(K key, V value);
public boolean replace(K key, V oldValue, V newValue);
}

View File

@ -32,9 +32,9 @@ public class AtomicReference<T> implements java.io.Serializable {
public T get() {
return value;
}
public void set(T newValue) {
this.value = newValue;
value = newValue;
}
public void lazySet(T newValue) {

View File

@ -0,0 +1,69 @@
/* Copyright (c) 2008-2014, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.util.concurrent.atomic;
import java.util.Arrays;
import sun.misc.Unsafe;
public class AtomicReferenceArray<T> {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long arrayOffset = unsafe.arrayBaseOffset(Object.class);
private static final long arrayScale = unsafe.arrayIndexScale(Object.class);
private final Object[] array;
public AtomicReferenceArray(int length) {
array = new Object[length];
}
public T get(int index) {
return (T) unsafe.getObjectVolatile
(array, arrayOffset + (index * arrayScale));
}
public void set(int index, T newValue) {
unsafe.putObjectVolatile
(array, arrayOffset + (index * arrayScale), newValue);
}
public void lazySet(int index, T newValue) {
unsafe.putOrderedObject
(array, arrayOffset + (index * arrayScale), newValue);
}
public boolean compareAndSet(int index, T expect, T update) {
return unsafe.compareAndSwapObject
(array, arrayOffset + (index * arrayScale), expect, update);
}
public boolean weakCompareAndSet(int index, T expect, T update) {
return compareAndSet(index, expect, update);
}
public final T getAndSet(int index, T newValue) {
while (true) {
T current = get(index);
if (compareAndSet(index, current, newValue)) {
return current;
}
}
}
public int length() {
return array.length;
}
@Override
public String toString() {
return Arrays.toString(array);
}
}

View File

@ -74,6 +74,8 @@ public final class Unsafe {
public native int arrayBaseOffset(Class arrayClass);
public native int arrayIndexScale(Class arrayClass);
public native long objectFieldOffset(Field field);
public native void park(boolean absolute, long time);

View File

@ -663,6 +663,28 @@ Avian_sun_misc_Unsafe_arrayBaseOffset
return ArrayBody;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_sun_misc_Unsafe_arrayIndexScale
(Thread* t, object, uintptr_t* arguments)
{
object c = jclassVmClass(t, reinterpret_cast<object>(arguments[1]));
if (c == type(t, Machine::JbooleanType)
|| c == type(t, Machine::JbyteType))
return 1;
else if (c == type(t, Machine::JshortType)
|| c == type(t, Machine::JcharType))
return 2;
else if (c == type(t, Machine::JintType)
|| c == type(t, Machine::JfloatType))
return 4;
else if (c == type(t, Machine::JlongType)
|| c == type(t, Machine::JdoubleType))
return 8;
else
return BytesPerWord;
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_java_nio_FixedArrayByteBuffer_allocateFixed
(Thread* t, object, uintptr_t* arguments)

View File

@ -2558,28 +2558,6 @@ Avian_sun_misc_Unsafe_staticFieldOffset
(t, jclassVmClass(t, jfieldClazz(t, jfield))), jfieldSlot(t, jfield)));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_sun_misc_Unsafe_arrayIndexScale
(Thread* t, object, uintptr_t* arguments)
{
switch (byteArrayBody
(t, className
(t, jclassVmClass(t, reinterpret_cast<object>(arguments[1]))), 1))
{
case 'Z':
case 'B': return 1;
case 'S':
case 'C': return 2;
case 'I':
case 'F': return 4;
case 'J':
case 'D': return 8;
case '[':
case 'L': return BytesPerWord;
default: abort(t);
}
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_sun_misc_Unsafe_staticFieldBase
(Thread* t, object, uintptr_t* arguments)

166
test/Concurrent.java Normal file
View File

@ -0,0 +1,166 @@
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
public class Concurrent {
private static final int ThreadCount = 4;
private static final int IterationCount = 100;
private static final int Range = 10;
private static final int CommonBase = -Range;
private static void expect(boolean v) {
if (! v) throw new RuntimeException();
}
public static void main(String[] args) throws Throwable {
final ConcurrentMap<Integer, Object> map = new ConcurrentHashMap();
final int[] counter = new int[1];
final int[] step = new int[1];
final Throwable[] exception = new Throwable[1];
synchronized (map) {
for (int i = 0; i < ThreadCount; ++i) {
final int index = i;
new Thread() {
public void run() {
try {
synchronized (map) {
++ counter[0];
map.notifyAll();
while (exception[0] == null && step[0] == 0) {
map.wait();
}
}
for (int i = 0; i < IterationCount; ++i) {
populateCommon(map);
populate(map, index * Range);
}
synchronized (map) {
-- counter[0];
map.notifyAll();
while (exception[0] == null && step[0] == 1) {
map.wait();
}
}
for (int i = 0; i < IterationCount; ++i) {
populate(map, index * Range);
depopulate(map, index * Range);
}
synchronized (map) {
++ counter[0];
map.notifyAll();
}
} catch (Throwable e) {
synchronized (map) {
exception[0] = e;
map.notifyAll();
}
e.printStackTrace();
}
}
}.start();
}
try {
while (exception[0] == null && counter[0] < ThreadCount) {
map.wait();
}
step[0] = 1;
map.notifyAll();
while (exception[0] == null && counter[0] > 0) {
map.wait();
}
if (map.size() != ThreadCount * Range) {
System.err.println
("expected " + (ThreadCount * Range) + " got " + map.size());
}
expect(map.size() == ThreadCount * Range);
for (int i = CommonBase, j = CommonBase + Range; i < j; ++i) {
expect(! map.containsKey(i));
}
step[0] = 2;
map.notifyAll();
while (exception[0] == null && counter[0] < ThreadCount) {
map.wait();
}
expect(map.isEmpty());
expect(exception[0] == null);
} catch (Throwable e) {
exception[0] = e;
throw e;
} finally {
map.notifyAll();
}
}
}
private static void populateCommon(ConcurrentMap<Integer, Object> map) {
Object value = new Object();
for (int i = CommonBase, j = CommonBase + Range; i < j; ++i) {
map.remove(i);
map.put(i, value);
map.remove(i);
}
}
private static void populate(ConcurrentMap<Integer, Object> map, int base) {
for (int i = base, j = base + Range; i < j; ++i) {
map.remove(i);
Object value = new Object();
expect(map.put(i, value) == null);
expect(map.containsKey(i));
expect(map.get(i).equals(value));
expect(map.putIfAbsent(i, new Object()) == value);
expect(map.get(i).equals(value));
expect(! map.remove(i, new Object()));
expect(map.remove(i, value));
expect(map.replace(i, value) == null);
expect(! map.containsKey(i));
expect(map.get(i) == null);
expect(map.putIfAbsent(i, value) == null);
expect(map.containsKey(i));
expect(map.get(i) == value);
Object newValue = new Object();
expect(map.replace(i, newValue) == value);
expect(map.get(i) == newValue);
boolean found = false;
for (Iterator<Map.Entry<Integer, Object>> it = map.entrySet().iterator();
it.hasNext();)
{
Map.Entry<Integer, Object> e = it.next();
if (e.getKey() == i) {
expect(! found);
expect(e.getValue() == newValue);
found = true;
it.remove();
}
}
expect(found);
expect(! map.containsKey(i));
expect(map.putIfAbsent(i, value) == null);
expect(map.containsKey(i));
expect(map.get(i) == value);
}
}
private static void depopulate(ConcurrentMap<Integer, Object> map, int base)
{
for (int i = base, j = base + Range; i < j; ++i) {
expect(map.containsKey(i));
expect(map.remove(i) != null);
}
}
}