Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.marshalling.reflect;
import static java.lang.Integer.bitCount;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* Lock-free concurrent hash map.
*
* @param the key type
* @param the value type
*
* @author David M. Lloyd
*/
final class UnlockedHashMap extends AbstractMap implements ConcurrentMap {
private static final int DEFAULT_INITIAL_CAPACITY = 512;
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final float DEFAULT_LOAD_FACTOR = 0.60f;
/** A row which has been resized into the new view. */
@SuppressWarnings("unchecked")
private static final Item,?>[] RESIZED = new Item[0];
/** A non-existent table entry (as opposed to a {@code null} value). */
private static final Object NONEXISTENT = new Object();
private volatile Table table;
private final Set keySet = new KeySet();
private final Set> entrySet = new EntrySet();
private final Collection values = new Values();
private final float loadFactor;
private final int initialCapacity;
@SuppressWarnings("unchecked")
private static final AtomicIntegerFieldUpdater
sizeUpdater = AtomicIntegerFieldUpdater.newUpdater(Table.class, "size");
@SuppressWarnings("unchecked")
private static final AtomicReferenceFieldUpdater tableUpdater = AtomicReferenceFieldUpdater.newUpdater(UnlockedHashMap.class, Table.class, "table");
@SuppressWarnings("unchecked")
private static final AtomicReferenceFieldUpdater valueUpdater = AtomicReferenceFieldUpdater.newUpdater(Item.class, Object.class, "value");
/**
* Construct a new instance.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
*/
public UnlockedHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Initial capacity must be > 0");
}
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
}
if (loadFactor <= 0.0 || Float.isNaN(loadFactor) || loadFactor >= 1.0) {
throw new IllegalArgumentException("Load factor must be between 0.0f and 1.0f");
}
int capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
this.loadFactor = loadFactor;
this.initialCapacity = capacity;
final Table table = new Table(capacity, loadFactor);
tableUpdater.set(this, table);
}
/**
* Construct a new instance.
*
* @param loadFactor the load factor
*/
public UnlockedHashMap(final float loadFactor) {
this(DEFAULT_INITIAL_CAPACITY, loadFactor);
}
/**
* Construct a new instance.
*
* @param initialCapacity the initial capacity
*/
public UnlockedHashMap(final int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Construct a new instance.
*/
public UnlockedHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
private Item[] addItem(final Item[] row, final Item newItem) {
if (row == null) {
return createRow(newItem);
} else {
final int length = row.length;
Item[] newRow = Arrays.copyOf(row, length + 1);
newRow[length] = newItem;
return newRow;
}
}
@SuppressWarnings("unchecked")
private static Item[] createRow(final Item newItem) {
return new Item[] { newItem };
}
@SuppressWarnings("unchecked")
private static Item[] createRow(final int length) {
return new Item[length];
}
private V doPut(K key, V value, boolean ifAbsent, Table table) {
final int hashCode = hashCode(key);
final int idx = hashCode & table.length() - 1;
OUTER: for (;;) {
// Fetch the table row.
Item[] oldRow = table.get(idx);
if (oldRow == RESIZED) {
// row was transported to the new table so recalculate everything
final V result = doPut(key, value, ifAbsent, table.resizeView);
// keep a consistent size view though!
if (result == NONEXISTENT) sizeUpdater.getAndIncrement(table);
return result;
}
if (oldRow != null) {
// Find the matching Item in the row.
Item oldItem = null;
for (Item tryItem : oldRow) {
if (key == tryItem.key) {
oldItem = tryItem;
break;
}
}
if (oldItem != null) {
// entry exists; try to return the old value and try to replace the value if allowed.
V oldItemValue;
do {
oldItemValue = oldItem.value;
if (oldItemValue == NONEXISTENT) {
// Key was removed; on the next iteration or two the doornail should be gone.
continue OUTER;
}
} while (! ifAbsent && ! valueUpdater.compareAndSet(oldItem, oldItemValue, value));
return oldItemValue;
}
// Row exists but item doesn't.
}
// Row doesn't exist, or row exists but item doesn't; try and add a new item to the row.
final Item newItem = new Item(key, hashCode, value);
final Item[] newRow = addItem(oldRow, newItem);
if (! table.compareAndSet(idx, oldRow, newRow)) {
// Nope, row changed; retry.
continue;
}
// Up the table size.
final int threshold = table.threshold;
int newSize = sizeUpdater.incrementAndGet(table);
// if table is resized, newSize will have the sign bit set and thus will be < 0
while (threshold < Integer.MAX_VALUE && newSize > threshold) {
if (sizeUpdater.compareAndSet(table, newSize, newSize | 0x80000000)) {
resize(table);
return nonexistent();
}
}
// Success.
return nonexistent();
}
}
private static int hashCode(final Object key) {
int h = key == null ? 0 : System.identityHashCode(key);
return h - (h << 7);
}
private void resize(Table origTable) {
final int origCapacity = origTable.length();
assert bitCount(origCapacity) == 1;
final Table newTable = new Table(origCapacity << 1, loadFactor);
// Prevent resize until we're done...
newTable.size = 0x80000000;
origTable.resizeView = newTable;
for (int i = 0; i < origCapacity; i ++) {
// for each row, try to resize into two new rows
Item[] origRow, newRow0 = null, newRow1 = null;
do {
origRow = origTable.get(i);
if (origRow != null) {
int count0 = 0, count1 = 0;
for (Item item : origRow) {
if ((item.hashCode & origCapacity) == 0) {
count0++;
} else {
count1++;
}
}
if (count0 != 0) {
newRow0 = createRow(count0);
int j = 0;
for (Item item : origRow) {
if ((item.hashCode & origCapacity) == 0) {
newRow0[j++] = item;
}
}
newTable.lazySet(i, newRow0);
} else {
newTable.lazySet(i, null);
}
if (count1 != 0) {
newRow1 = createRow(count1);
int j = 0;
for (Item item : origRow) {
if ((item.hashCode & origCapacity) != 0) {
newRow1[j++] = item;
}
}
newTable.lazySet(i + origCapacity, newRow1);
} else {
newTable.lazySet(i + origCapacity, null);
}
}
} while (! origTable.compareAndSet(i, origRow, UnlockedHashMap.resized()));
if (origRow != null) sizeUpdater.getAndAdd(newTable, origRow.length);
}
int size;
do {
size = newTable.size;
final int threshold = newTable.threshold;
if (threshold < Integer.MAX_VALUE && (size & 0x7fffffff) >= threshold) {
// time for another resize, right away
resize(newTable);
return;
}
} while (!sizeUpdater.compareAndSet(newTable, size, size & 0x7fffffff));
// All done, plug in the new table
table = newTable;
}
private static Item[] remove(Item[] row, int idx) {
final int len = row.length;
assert idx < len;
if (len == 1) {
return null;
}
@SuppressWarnings("unchecked")
Item[] newRow = new Item[len - 1];
if (idx > 0) {
System.arraycopy(row, 0, newRow, 0, idx);
}
if (idx < len - 1) {
System.arraycopy(row, idx + 1, newRow, idx, len - 1 - idx);
}
return newRow;
}
public V putIfAbsent(final K key, final V value) {
final V result = doPut(key, value, true, table);
return result == NONEXISTENT ? null : result;
}
public boolean remove(final Object objectKey, final Object objectValue) {
// Get type-safe key and value.
@SuppressWarnings("unchecked")
final K key = (K) objectKey;
@SuppressWarnings("unchecked")
final V value = (V) objectValue;
return doRemove(key, value, table);
}
private boolean doRemove(final Item item, final Table table) {
int hashCode = item.hashCode;
final int idx = hashCode & table.length() - 1;
Item[] oldRow;
for (;;) {
oldRow = table.get(idx);
if (oldRow == null) {
return false;
}
if (oldRow == RESIZED) {
boolean result;
if (result = doRemove(item, table.resizeView)) {
sizeUpdater.getAndDecrement(table);
}
return result;
}
int rowIdx = -1;
for (int i = 0; i < oldRow.length; i ++) {
if (item == oldRow[i]) {
rowIdx = i;
break;
}
}
if (rowIdx == -1) {
return false;
}
if (table.compareAndSet(idx, oldRow, remove(oldRow, rowIdx))) {
sizeUpdater.getAndDecrement(table);
return true;
}
// row changed, cycle back again
}
}
private boolean doRemove(final K key, final V value, final Table table) {
final int hashCode = hashCode(key);
final int idx = hashCode & table.length() - 1;
Item[] oldRow;
// Fetch the table row.
oldRow = table.get(idx);
if (oldRow == null) {
// no match for the key
return false;
}
if (oldRow == RESIZED) {
boolean result;
if (result = doRemove(key, value, table.resizeView)) {
// keep size consistent
sizeUpdater.getAndDecrement(table);
}
return result;
}
// Find the matching Item in the row.
Item oldItem = null;
V oldValue = null;
int rowIdx = -1;
for (int i = 0; i < oldRow.length; i ++) {
Item tryItem = oldRow[i];
if (key == tryItem.key) {
final Object v2 = oldValue = tryItem.value;
if (value == v2) {
oldItem = tryItem;
rowIdx = i;
break;
} else {
// value doesn't match; exit without changing map.
return false;
}
}
}
if (oldItem == null) {
// no such entry exists.
return false;
}
if (! valueUpdater.compareAndSet(oldItem, oldValue, NONEXISTENT)) {
// Value was changed to a non-equal value.
return false;
}
// Now we are free to remove the item from the row.
if (table.compareAndSet(idx, oldRow, remove(oldRow, rowIdx))) {
// Adjust the table size, since we are definitely the ones to be removing this item from the table.
sizeUpdater.decrementAndGet(table);
return true;
} else {
// The old row changed so retry by the other algorithm
return doRemove(oldItem, table);
}
}
@SuppressWarnings("unchecked")
public V remove(final Object objectKey) {
final V result = doRemove((K) objectKey, table);
return result == NONEXISTENT ? null : result;
}
private V doRemove(final K key, final Table table) {
final int hashCode = hashCode(key);
final int idx = hashCode & table.length() - 1;
// Fetch the table row.
Item[] oldRow = table.get(idx);
if (oldRow == null) {
// no match for the key
return nonexistent();
}
if (oldRow == RESIZED) {
V result;
if ((result = doRemove(key, table.resizeView)) != NONEXISTENT) {
// keep size consistent
sizeUpdater.getAndDecrement(table);
}
return result;
}
// Find the matching Item in the row.
Item oldItem = null;
int rowIdx = -1;
for (int i = 0; i < oldRow.length; i ++) {
Item tryItem = oldRow[i];
if (key == tryItem.key) {
oldItem = tryItem;
rowIdx = i;
break;
}
}
if (oldItem == null) {
// no such entry exists.
return nonexistent();
}
// Mark the item as "removed".
@SuppressWarnings("unchecked")
V oldValue = (V) valueUpdater.getAndSet(oldItem, NONEXISTENT);
if (oldValue == NONEXISTENT) {
// Someone else beat us to it.
return nonexistent();
}
// Now we are free to remove the item from the row.
if (table.compareAndSet(idx, oldRow, remove(oldRow, rowIdx))) {
// Adjust the table size, since we are definitely the ones to be removing this item from the table.
sizeUpdater.decrementAndGet(table);
// Item is removed from the row; we are done here.
return oldValue;
} else {
boolean result = doRemove(oldItem, table);
assert result;
return oldValue;
}
}
@SuppressWarnings("unchecked")
private static V nonexistent() {
return (V) NONEXISTENT;
}
@SuppressWarnings("unchecked")
private static Item[] resized() {
return (Item[]) RESIZED;
}
public boolean replace(final K key, final V oldValue, final V newValue) {
return doReplace(key, oldValue, newValue, table);
}
private boolean doReplace(final K key, final V oldValue, final V newValue, final Table table) {
final int hashCode = hashCode(key);
final int idx = hashCode & table.length() - 1;
// Fetch the table row.
Item[] oldRow = table.get(idx);
if (oldRow == null) {
// no match for the key
return false;
}
if (oldRow == RESIZED) {
return doReplace(key, oldValue, newValue, table.resizeView);
}
// Find the matching Item in the row.
Item oldItem = null;
V oldRowValue = null;
for (Item tryItem : oldRow) {
if (key == tryItem.key) {
final Object v2 = oldRowValue = tryItem.value;
if (oldValue == v2) {
oldItem = tryItem;
break;
} else {
// value doesn't match; exit without changing map.
return false;
}
}
}
if (oldItem == null) {
// no such entry exists.
return false;
}
// Now swap the item.
return valueUpdater.compareAndSet(oldItem, oldRowValue, newValue);
}
public V replace(final K key, final V value) {
final V result = doReplace(key, value, table);
return result == NONEXISTENT ? null : result;
}
private V doReplace(final K key, final V value, final Table table) {
final int hashCode = hashCode(key);
final int idx = hashCode & table.length() - 1;
// Fetch the table row.
Item[] oldRow = table.get(idx);
if (oldRow == null) {
// no match for the key
return nonexistent();
}
if (oldRow == RESIZED) {
return doReplace(key, value, table.resizeView);
}
// Find the matching Item in the row.
Item oldItem = null;
for (Item tryItem : oldRow) {
if (key == tryItem.key) {
oldItem = tryItem;
break;
}
}
if (oldItem == null) {
// no such entry exists.
return nonexistent();
}
// Now swap the item.
@SuppressWarnings("unchecked")
V oldRowValue = (V) valueUpdater.getAndSet(oldItem, value);
if (oldRowValue == NONEXISTENT) {
// Item was removed.
return nonexistent();
}
// Item is swapped; we are done here.
return oldRowValue;
}
public int size() {
return table.size & 0x7fffffff;
}
private V doGet(final Table table, final K key) {
final Item[] row = table.get(hashCode(key) & (table.length() - 1));
if (row != null) for (Item item : row) {
if (key == item.key) {
return item.value;
}
}
return nonexistent();
}
public boolean containsKey(final Object key) {
@SuppressWarnings("unchecked")
final V value = doGet(table, (K) key);
return value != NONEXISTENT;
}
public V get(final Object key) {
@SuppressWarnings("unchecked")
final V value = doGet(table, (K) key);
return value == NONEXISTENT ? null : value;
}
public V put(final K key, final V value) {
final V result = doPut(key, value, false, table);
return result == NONEXISTENT ? null : result;
}
public void clear() {
table = new Table(initialCapacity, loadFactor);
}
public Set> entrySet() {
return entrySet;
}
public Collection values() {
return values;
}
public Set keySet() {
return keySet;
}
final class KeySet extends AbstractSet implements Set {
public void clear() {
UnlockedHashMap.this.clear();
}
public boolean contains(final Object o) {
return containsKey(o);
}
@SuppressWarnings("unchecked")
public boolean remove(final Object o) {
return doRemove((K) o, table) != NONEXISTENT;
}
public Iterator iterator() {
return new KeyIterator();
}
public Object[] toArray() {
ArrayList