package hashtables.lockfree;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import sun.misc.Unsafe;
import contention.abstractions.CompositionalMap;
import contention.abstractions.MaintenanceAlg;
import hashtables.lockfree.cliffutils.UtilUnsafe;
/**
* This code corresponds to the contention-friendly hash map
* as described in:
*
* T. Crain, V. Gramoli and M. Raynal. A Contention-Friendly
* Methodology for Search Structures. TR #hal-00668010, INRIA,
* 2012.
*
* @author Tyler Crain
*/
public class NonBlockingFriendlyHashMap<K, V> implements
CompositionalMap<K, V>, MaintenanceAlg {
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
private long structMods = 0;
volatile boolean stop = false;
private MaintenanceThread mainThd;
private class MaintenanceThread<K, V> extends Thread {
NonBlockingFriendlyHashMap<K, V> map;
MaintenanceThread(NonBlockingFriendlyHashMap<K, V> map) {
this.map = map;
}
public void run() {
map.doMaintenance();
}
}
private static long rawIndex(final Object[] ary, final int idx) {
assert idx >= 0 && idx < ary.length;
return _Obase + idx * _Oscale;
}
public void doMaintenance() {
int size;
while (!stop) {
size = size();
if (size > threshold) {
rehash();
}
}
}
public boolean startMaintenance() {
this.stop = false;
mainThd = new MaintenanceThread<K, V>(this);
mainThd.start();
return true;
}
public boolean stopMaintenance() {
this.stop = true;
try {
this.mainThd.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
// --- Bits to allow Unsafe access to arrays
private static Unsafe _unsafe = UtilUnsafe.getUnsafe();
private static final int _Obase = _unsafe.arrayBaseOffset(Object[].class);
private static final int _Oscale = _unsafe.arrayIndexScale(Object[].class);
private static final boolean CAS_val(Object[] kvs, int idx, Object old,
Object val) {
// Do I need to shift the index bit, like done in the lock free table???
return _unsafe
.compareAndSwapObject(kvs, rawIndex(kvs, (idx)), old, val);
}
int threshold;
float loadFactor;
int cap;
class Table {
final transient HashEntry<K, V>[] table;
final HashEntry<K, V> dummy;
public Table(int size) {
this.table = new HashEntry[size];
this.dummy = new HashEntry<K, V>(null, 0, null, null);
// for(int i = 0; i < this.table.length; i++) {
// table[i] = null;
// }
}
}
volatile transient Table table1, table2;
static final class HashEntry<K, V> {
final K key;
final int hash;
volatile V value;
final HashEntry<K, V> next;
HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
this.key = key;
this.hash = hash;
this.next = next;
this.value = value;
}
@SuppressWarnings("unchecked")
static final <K, V> HashEntry<K, V>[] newArray(int i) {
return new HashEntry[i];
}
}
public NonBlockingFriendlyHashMap(int initialCapacity, float loadFactor) {
if (!(loadFactor > 0) || initialCapacity < 0)
throw new IllegalArgumentException();
cap = 1;
while (cap < initialCapacity)
cap <<= 1;
this.loadFactor = loadFactor;
this.table1 = new Table(cap);
this.threshold = (int) (table1.table.length * loadFactor);
this.startMaintenance();
}
public NonBlockingFriendlyHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public NonBlockingFriendlyHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
Table getTable(Table oldTable) {
Table tab2 = table2;
if (oldTable == table1)
return tab2;
return table1;
}
HashEntry<K, V> getFirst(int hash) {
Table table = table1;
HashEntry<K, V>[] tab = table.table;
HashEntry<K, V> node = tab[hash & (tab.length - 1)];
while (node == table.dummy) {
table = getTable(table);
tab = table.table;
node = tab[hash & (tab.length - 1)];
}
return node;
}
private static int hash(int h) {
// Spread bits to regularize both segment and index locations,
// using variant of single-word Wang/Jenkins hash.
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}
@Override
public boolean containsKey(Object key) {
if (this.get(key) == null) {
return false;
}
return true;
}
@Override
public V remove(final Object key) {
HashEntry<K, V>[] tab;
// int hash = hash(key.hashCode());
int hash = key.hashCode();
Table table;
V oldValue;
int index;
HashEntry<K, V> first, e;
while (true) {
table = table1;
tab = table.table;
index = hash & (tab.length - 1);
first = tab[index];
while (first == table.dummy) {
table = getTable(table);
tab = table.table;
index = hash & (tab.length - 1);
first = tab[index];
}
e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
oldValue = null;
if (e != null) {
oldValue = e.value;
// All entries following removed node can stay
// in list, but all preceding ones need to be
// cloned.
HashEntry<K, V> newFirst = e.next;
for (HashEntry<K, V> p = first; p != e; p = p.next)
newFirst = new HashEntry<K, V>(p.key, p.hash, newFirst,
p.value);
if (CAS_val(tab, index, first, newFirst)) {
break;
}
} else {
break;
}
}
return oldValue;
}
private void rehash() {
int oldCapacity = table1.table.length;
table2 = new Table(oldCapacity << 1);
threshold = (int) (table2.table.length * loadFactor);
int sizeMask = table2.table.length - 1;
for (int i = 0; i < oldCapacity; i++) {
rehashLevel(i, sizeMask);
}
// System.out.println("done rehash");
table1 = table2;
if (STRUCT_MODS)
this.structMods += 1;
}
private void rehashLevel(int i, int sizeMask) {
HashEntry<K, V> e, dummy = table1.dummy;
HashEntry<K, V>[] oldTable = table1.table;
HashEntry<K, V>[] newTable = table2.table;
while (true) {
newTable[i] = null;
newTable[i + table1.table.length] = null;
e = oldTable[i];
if (e != null) {
HashEntry<K, V> next = e.next;
int idx = e.hash & sizeMask;
// Single node on list
if (next == null)
newTable[idx] = e;
else {
// Reuse trailing consecutive sequence at same slot
HashEntry<K, V> lastRun = e;
int lastIdx = idx;
for (HashEntry<K, V> last = next; last != null; last = last.next) {
int k = last.hash & sizeMask;
if (k != lastIdx) {
lastIdx = k;
lastRun = last;
}
}
newTable[lastIdx] = lastRun;
// Clone all remaining nodes
for (HashEntry<K, V> p = e; p != lastRun; p = p.next) {
int k = p.hash & sizeMask;
HashEntry<K, V> n = newTable[k];
newTable[k] = new HashEntry<K, V>(p.key, p.hash, n,
p.value);
}
}
}
if (CAS_val(oldTable, i, e, dummy)) {
break;
}
}
}
@Override
public V putIfAbsent(K key, V value) {
HashEntry<K, V>[] tab;
// int hash = hash(key.hashCode());
int hash = key.hashCode();
Table table;
V oldValue;
int index;
HashEntry<K, V> first, e;
while (true) {
table = table1;
tab = table.table;
index = hash & (tab.length - 1);
first = tab[index];
while (first == table.dummy) {
table = getTable(table);
tab = table.table;
index = hash & (tab.length - 1);
first = tab[index];
}
e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
if (e != null) {
oldValue = e.value;
break;
} else {
oldValue = null;
HashEntry<K, V> newEntry = new HashEntry<K, V>(key, hash,
first, value);
if (CAS_val(tab, index, first, newEntry)) {
break;
}
}
}
return oldValue;
}
@Override
public void clear() {
this.stopMaintenance();
this.structMods = 0;
HashEntry<K, V>[] tab = table1.table;
for (int i = 0; i < tab.length; i++) {
tab[i] = null;
}
this.startMaintenance();
}
@Override
public int size() {
HashEntry<K, V>[] tab = table1.table;
HashEntry<K, V> next;
int count = 0;
for (int i = 0; i < tab.length; i++) {
next = tab[i];
while (next != null) {
count++;
next = next.next;
}
}
return count;
}
@Override
public V get(final Object key) {
// int hash = hash(key.hashCode());
int hash = key.hashCode();
HashEntry<K, V> e = getFirst(hash);
while (e != null) {
if (e.hash == hash && key.equals(e.key)) {
V v = e.value;
if (v != null)
return v;
// is the following really necessary?
while (v == null) {
v = e.value;
}
return v;
}
e = e.next;
}
return null;
}
@Override
public boolean containsValue(Object value) {
// TODO Auto-generated method stub
return false;
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return false;
}
@Override
public Set<K> keySet() {
// TODO Auto-generated method stub
return null;
}
@Override
public V put(K key, V value) {
// TODO Auto-generated method stub
return null;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
// TODO Auto-generated method stub
}
@Override
public Collection<V> values() {
// TODO Auto-generated method stub
return null;
}
@Override
public long getStructMods() {
return structMods;
}
@Override
public int numNodes() {
// TODO Auto-generated method stub
return 0;
}
}