More optimizations
This commit is contained in:
parent
3acd8f934f
commit
67c6403355
@ -1,11 +1,8 @@
|
|||||||
package dorkbox.util.messagebus.common;
|
package dorkbox.util.messagebus.common;
|
||||||
|
|
||||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
import java.util.Map;
|
||||||
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,14 +14,13 @@ import java.util.concurrent.locks.Lock;
|
|||||||
* Date: 2/2/15
|
* Date: 2/2/15
|
||||||
*/
|
*/
|
||||||
public class HashMapTree<KEY, VALUE> {
|
public class HashMapTree<KEY, VALUE> {
|
||||||
private final ReentrantReadWriteUpdateLock lock = new ReentrantReadWriteUpdateLock();
|
private Map<KEY, HashMapTree<KEY, VALUE>> children;
|
||||||
|
private volatile VALUE value;
|
||||||
|
|
||||||
private ConcurrentMap<KEY, HashMapTree<KEY, VALUE>> children; // protected by read/write lock
|
|
||||||
private volatile VALUE value; // protected by read/write lock
|
|
||||||
private final int defaultSize;
|
private final int defaultSize;
|
||||||
private final float loadFactor;
|
private final float loadFactor;
|
||||||
|
|
||||||
public HashMapTree(int defaultSize, float loadFactor) {
|
public HashMapTree(final int defaultSize, final float loadFactor) {
|
||||||
this.defaultSize = defaultSize;
|
this.defaultSize = defaultSize;
|
||||||
this.loadFactor = loadFactor;
|
this.loadFactor = loadFactor;
|
||||||
}
|
}
|
||||||
@ -33,30 +29,26 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
/**
|
/**
|
||||||
* can be overridden to provide a custom backing map
|
* can be overridden to provide a custom backing map
|
||||||
*/
|
*/
|
||||||
protected ConcurrentMap<KEY, HashMapTree<KEY, VALUE>> createChildren(int defaultSize, float loadFactor) {
|
protected Map<KEY, HashMapTree<KEY, VALUE>> createChildren(int defaultSize, float loadFactor) {
|
||||||
return new ConcurrentHashMapV8<KEY, HashMapTree<KEY, VALUE>>(defaultSize, loadFactor, 1);
|
return new ConcurrentHashMapV8<KEY, HashMapTree<KEY, VALUE>>(defaultSize, loadFactor, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE getValue() {
|
public final VALUE getValue() {
|
||||||
VALUE returnValue = this.value;
|
return this.value;
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void putValue(VALUE value) {
|
public final void putValue(VALUE value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeValue() {
|
public final void removeValue() {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void clear() {
|
public final void clear() {
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
if (this.children != null) {
|
if (this.children != null) {
|
||||||
Set<Entry<KEY, HashMapTree<KEY, VALUE>>> entrySet = this.children.entrySet();
|
Set<Entry<KEY, HashMapTree<KEY, VALUE>>> entrySet = this.children.entrySet();
|
||||||
for (Entry<KEY, HashMapTree<KEY, VALUE>> entry : entrySet) {
|
for (Entry<KEY, HashMapTree<KEY, VALUE>> entry : entrySet) {
|
||||||
@ -66,8 +58,6 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
this.children.clear();
|
this.children.clear();
|
||||||
this.value = null;
|
this.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -76,7 +66,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Removes a branch from the tree, and cleans up, if necessary
|
* Removes a branch from the tree, and cleans up, if necessary
|
||||||
*/
|
*/
|
||||||
public void remove(KEY key) {
|
public final void remove(KEY key) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
removeLeaf(key);
|
removeLeaf(key);
|
||||||
}
|
}
|
||||||
@ -88,35 +78,24 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Removes a branch from the tree, and cleans up, if necessary
|
* Removes a branch from the tree, and cleans up, if necessary
|
||||||
*/
|
*/
|
||||||
public void remove(KEY key1, KEY key2) {
|
public final void remove(KEY key1, KEY key2) {
|
||||||
if (key1 == null || key2 == null) {
|
if (key1 == null || key2 == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock UPDATE = this.lock.updateLock();
|
HashMapTree<KEY, VALUE> leaf;
|
||||||
UPDATE.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> leaf = null;
|
|
||||||
if (this.children != null) {
|
if (this.children != null) {
|
||||||
leaf = this.children.get(key1);
|
leaf = this.children.get(key1);
|
||||||
|
|
||||||
if (leaf != null) {
|
if (leaf != null) {
|
||||||
// promote to writelock and try again - Concurrency in Practice,16.4.2, last sentence on page. Careful for stale state
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
leaf.removeLeaf(key2);
|
leaf.removeLeaf(key2);
|
||||||
this.children.remove(key1);
|
this.children.remove(key1);
|
||||||
|
|
||||||
if (this.children.isEmpty()) {
|
if (this.children.isEmpty()) {
|
||||||
this.children = null;
|
this.children = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UPDATE.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,35 +103,24 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Removes a branch from the tree, and cleans up, if necessary
|
* Removes a branch from the tree, and cleans up, if necessary
|
||||||
*/
|
*/
|
||||||
public void remove(KEY key1, KEY key2, KEY key3) {
|
public final void remove(KEY key1, KEY key2, KEY key3) {
|
||||||
if (key1 == null || key2 == null) {
|
if (key1 == null || key2 == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock UPDATE = this.lock.updateLock();
|
HashMapTree<KEY, VALUE> leaf;
|
||||||
UPDATE.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> leaf = null;
|
|
||||||
if (this.children != null) {
|
if (this.children != null) {
|
||||||
leaf = this.children.get(key1);
|
leaf = this.children.get(key1);
|
||||||
|
|
||||||
if (leaf != null) {
|
if (leaf != null) {
|
||||||
// promote to writelock and try again - Concurrency in Practice,16.4.2, last sentence on page. Careful for stale state
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
leaf.remove(key2, key3);
|
leaf.remove(key2, key3);
|
||||||
this.children.remove(key1);
|
this.children.remove(key1);
|
||||||
|
|
||||||
if (this.children.isEmpty()) {
|
if (this.children.isEmpty()) {
|
||||||
this.children = null;
|
this.children = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UPDATE.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +130,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
* Removes a branch from the tree, and cleans up, if necessary
|
* Removes a branch from the tree, and cleans up, if necessary
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void remove(KEY... keys) {
|
public final void remove(KEY... keys) {
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -174,11 +142,8 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
/**
|
/**
|
||||||
* Removes a branch from the tree, and cleans up, if necessary
|
* Removes a branch from the tree, and cleans up, if necessary
|
||||||
*/
|
*/
|
||||||
private final void removeLeaf(KEY key) {
|
private void removeLeaf(KEY key) {
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
if (this.children != null) {
|
if (this.children != null) {
|
||||||
HashMapTree<KEY, VALUE> leaf = this.children.get(key);
|
HashMapTree<KEY, VALUE> leaf = this.children.get(key);
|
||||||
|
|
||||||
@ -195,16 +160,11 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keys CANNOT be null here!
|
// keys CANNOT be null here!
|
||||||
private final void removeLeaf(int index, KEY[] keys) {
|
private void removeLeaf(int index, KEY[] keys) {
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
if (index == keys.length) {
|
if (index == keys.length) {
|
||||||
// we have reached the leaf to remove!
|
// we have reached the leaf to remove!
|
||||||
this.value = null;
|
this.value = null;
|
||||||
@ -223,249 +183,89 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE put(VALUE value, KEY key) {
|
public final VALUE put(VALUE value, KEY key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new NullPointerException("keys");
|
throw new NullPointerException("keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
// have to put value into our children
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key);
|
HashMapTree<KEY, VALUE> leaf = createLeaf(key);
|
||||||
VALUE prev = leaf.value;
|
VALUE prev = leaf.value;
|
||||||
leaf.value = value;
|
leaf.value = value;
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE putIfAbsent(VALUE value, KEY key) {
|
public final VALUE put(VALUE value, KEY key1, KEY key2) {
|
||||||
if (key == null) {
|
|
||||||
throw new NullPointerException("keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key);
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
|
||||||
if (prev == null) {
|
|
||||||
leaf.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VALUE put(VALUE value, KEY key1, KEY key2) {
|
|
||||||
if (key1 == null || key2 == null) {
|
if (key1 == null || key2 == null) {
|
||||||
throw new NullPointerException("keys");
|
throw new NullPointerException("keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
// have to put value into our children
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key1);
|
HashMapTree<KEY, VALUE> leaf = createLeaf(key1);
|
||||||
Lock WRITE2 = leaf.lock.writeLock();
|
leaf = leaf.createLeaf(key2);
|
||||||
WRITE2.lock();
|
|
||||||
leaf = leaf.createLeaf_NL(key2);
|
|
||||||
WRITE2.unlock();
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
VALUE prev = leaf.value;
|
||||||
leaf.value = value;
|
leaf.value = value;
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE putIfAbsent(VALUE value, KEY key1, KEY key2) {
|
public final VALUE put(VALUE value, KEY key1, KEY key2, KEY key3) {
|
||||||
if (key1 == null || key2 == null) {
|
|
||||||
throw new NullPointerException("keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key1);
|
|
||||||
Lock WRITE2 = leaf.lock.writeLock();
|
|
||||||
WRITE2.lock();
|
|
||||||
leaf = leaf.createLeaf_NL(key2);
|
|
||||||
WRITE2.unlock();
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
|
||||||
if (prev == null) {
|
|
||||||
leaf.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VALUE put(VALUE value, KEY key1, KEY key2, KEY key3) {
|
|
||||||
if (key1 == null || key2 == null || key3 == null) {
|
if (key1 == null || key2 == null || key3 == null) {
|
||||||
throw new NullPointerException("keys");
|
throw new NullPointerException("keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
// have to put value into our children
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key1);
|
HashMapTree<KEY, VALUE> leaf = createLeaf(key1);
|
||||||
Lock WRITE2 = leaf.lock.writeLock();
|
leaf = leaf.createLeaf(key2);
|
||||||
WRITE2.lock();
|
leaf = leaf.createLeaf(key3);
|
||||||
leaf = leaf.createLeaf_NL(key2);
|
|
||||||
Lock WRITE3 = leaf.lock.writeLock();
|
|
||||||
WRITE3.lock();
|
|
||||||
leaf = leaf.createLeaf_NL(key3);
|
|
||||||
WRITE3.unlock();
|
|
||||||
WRITE2.unlock();
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
VALUE prev = leaf.value;
|
||||||
leaf.value = value;
|
leaf.value = value;
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE putIfAbsent(VALUE value, KEY key1, KEY key2, KEY key3) {
|
public final VALUE put(VALUE value, KEY... keys) {
|
||||||
if (key1 == null || key2 == null || key3 == null) {
|
|
||||||
throw new NullPointerException("keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(key1);
|
|
||||||
Lock WRITE2 = leaf.lock.writeLock();
|
|
||||||
WRITE2.lock();
|
|
||||||
leaf = leaf.createLeaf_NL(key2);
|
|
||||||
Lock WRITE3 = leaf.lock.writeLock();
|
|
||||||
WRITE3.lock();
|
|
||||||
leaf = leaf.createLeaf_NL(key3);
|
|
||||||
WRITE3.unlock();
|
|
||||||
WRITE2.unlock();
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
|
||||||
if (prev == null) {
|
|
||||||
leaf.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public VALUE put(VALUE value, KEY... keys) {
|
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
throw new NullPointerException("keys");
|
throw new NullPointerException("keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = keys.length;
|
int length = keys.length;
|
||||||
Lock[] locks = new Lock[length];
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
// have to put value into our children
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(keys[0]);
|
HashMapTree<KEY, VALUE> leaf = createLeaf(keys[0]);
|
||||||
for (int i=1;i<length;i++) {
|
for (int i=1;i<length;i++) {
|
||||||
locks[i] = leaf.lock.writeLock();
|
leaf = leaf.createLeaf(keys[i]);
|
||||||
locks[i].lock();
|
|
||||||
leaf = leaf.createLeaf_NL(keys[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=length-1;i>0;i--) {
|
|
||||||
locks[i].unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
VALUE prev = leaf.value;
|
||||||
leaf.value = value;
|
leaf.value = value;
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public final HashMapTree<KEY, VALUE> createLeaf(KEY... keys) {
|
||||||
public VALUE putIfAbsent(VALUE value, KEY... keys) {
|
|
||||||
if (keys == null) {
|
|
||||||
throw new NullPointerException("keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = keys.length;
|
|
||||||
Lock[] locks = new Lock[length];
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(keys[0]);
|
|
||||||
for (int i=1;i<length;i++) {
|
|
||||||
locks[i] = leaf.lock.writeLock();
|
|
||||||
locks[i].lock();
|
|
||||||
leaf = leaf.createLeaf_NL(keys[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=length-1;i>0;i--) {
|
|
||||||
locks[i].unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE prev = leaf.value;
|
|
||||||
if (prev == null) {
|
|
||||||
leaf.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public HashMapTree<KEY, VALUE> createLeaf(KEY... keys) {
|
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
int length = keys.length;
|
|
||||||
Lock[] locks = new Lock[length];
|
|
||||||
|
|
||||||
Lock WRITE = this.lock.writeLock();
|
int length = keys.length;
|
||||||
WRITE.lock(); // upgrade to the write lock, at this point blocks other readers
|
|
||||||
|
|
||||||
// have to put value into our children
|
// have to put value into our children
|
||||||
HashMapTree<KEY, VALUE> leaf = createLeaf_NL(keys[0]);
|
HashMapTree<KEY, VALUE> leaf = createLeaf(keys[0]);
|
||||||
for (int i=1;i<length;i++) {
|
for (int i=1;i<length;i++) {
|
||||||
locks[i] = leaf.lock.writeLock();
|
leaf = leaf.createLeaf(keys[i]);
|
||||||
locks[i].lock();
|
|
||||||
leaf = leaf.createLeaf_NL(keys[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=length-1;i>0;i--) {
|
|
||||||
locks[i].unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE.unlock();
|
|
||||||
|
|
||||||
return leaf;
|
return leaf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final HashMapTree<KEY, VALUE> createLeaf_NL(KEY key) {
|
private HashMapTree<KEY, VALUE> createLeaf(KEY key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -497,105 +297,76 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
|
|
||||||
public VALUE get(KEY key) {
|
public final VALUE get(KEY key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock READ = this.lock.readLock();
|
HashMapTree<KEY, VALUE> objectTree;
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> objectTree = null;
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
objectTree = getLeaf_NL(key); // protected by lock
|
objectTree = getLeaf(key); // protected by lock
|
||||||
|
|
||||||
if (objectTree == null) {
|
if (objectTree == null) {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE returnValue = objectTree.value;
|
return objectTree.value;
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE get(KEY key1, KEY key2) {
|
public final VALUE get(KEY key1, KEY key2) {
|
||||||
Lock READ = this.lock.readLock();
|
HashMapTree<KEY, VALUE> tree;
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(key1); // protected by lock
|
tree = getLeaf(key1); // protected by lock
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key2); // protected by lock
|
tree = tree.getLeaf(key2); // protected by lock
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree == null) {
|
if (tree == null) {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE returnValue = tree.value;
|
return tree.value;
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VALUE getValue(KEY key1, KEY key2, KEY key3) {
|
public final VALUE getValue(KEY key1, KEY key2, KEY key3) {
|
||||||
Lock READ = this.lock.readLock();
|
HashMapTree<KEY, VALUE> tree;
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(key1);
|
tree = getLeaf(key1);
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key2);
|
tree = tree.getLeaf(key2);
|
||||||
}
|
}
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key3);
|
tree = tree.getLeaf(key3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree == null) {
|
if (tree == null) {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE returnValue = tree.value;
|
return tree.value;
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public VALUE get(KEY... keys) {
|
public final VALUE get(KEY... keys) {
|
||||||
Lock READ = this.lock.readLock();
|
HashMapTree<KEY, VALUE> tree;
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(keys[0]);
|
tree = getLeaf(keys[0]);
|
||||||
|
|
||||||
int size = keys.length;
|
int size = keys.length;
|
||||||
for (int i=1;i<size;i++) {
|
for (int i=1;i<size;i++) {
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(keys[i]);
|
tree = tree.getLeaf(keys[i]);
|
||||||
} else {
|
} else {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tree == null) {
|
if (tree == null) {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE returnValue = tree.value;
|
return tree.value;
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final HashMapTree<KEY, VALUE> getLeaf(KEY key) {
|
public final HashMapTree<KEY, VALUE> getLeaf(KEY key) {
|
||||||
@ -605,54 +376,39 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
|
|
||||||
HashMapTree<KEY, VALUE> tree;
|
HashMapTree<KEY, VALUE> tree;
|
||||||
|
|
||||||
Lock READ = this.lock.readLock();
|
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
if (this.children == null) {
|
if (this.children == null) {
|
||||||
tree = null;
|
tree = null;
|
||||||
} else {
|
} else {
|
||||||
tree = this.children.get(key);
|
tree = this.children.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2) {
|
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2) {
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
HashMapTree<KEY, VALUE> tree;
|
||||||
|
|
||||||
Lock READ = this.lock.readLock();
|
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(key1);
|
tree = getLeaf(key1);
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key2);
|
tree = tree.getLeaf(key2);
|
||||||
}
|
}
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2, KEY key3) {
|
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2, KEY key3) {
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
HashMapTree<KEY, VALUE> tree;
|
||||||
|
|
||||||
Lock READ = this.lock.readLock();
|
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(key1);
|
tree = getLeaf(key1);
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key2);
|
tree = tree.getLeaf(key2);
|
||||||
}
|
}
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(key3);
|
tree = tree.getLeaf(key3);
|
||||||
}
|
}
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,36 +420,18 @@ public class HashMapTree<KEY, VALUE> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock READ = this.lock.readLock();
|
HashMapTree<KEY, VALUE> tree;
|
||||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
|
||||||
|
|
||||||
HashMapTree<KEY, VALUE> tree = null;
|
|
||||||
// publish value from our children
|
// publish value from our children
|
||||||
tree = getLeaf_NL(keys[0]);
|
tree = getLeaf(keys[0]);
|
||||||
|
|
||||||
for (int i=1;i<size;i++) {
|
for (int i=1;i<size;i++) {
|
||||||
if (tree != null) {
|
if (tree != null) {
|
||||||
tree = tree.getLeaf_NL(keys[i]);
|
tree = tree.getLeaf(keys[i]);
|
||||||
} else {
|
} else {
|
||||||
READ.unlock();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
READ.unlock();
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final HashMapTree<KEY, VALUE> getLeaf_NL(KEY key) {
|
|
||||||
HashMapTree<KEY, VALUE> tree;
|
|
||||||
|
|
||||||
if (this.children == null) {
|
|
||||||
tree = null;
|
|
||||||
} else {
|
|
||||||
tree = this.children.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,5 +61,5 @@ public interface IHandlerInvocation {
|
|||||||
* type that the handler consumes
|
* type that the handler consumes
|
||||||
* @param handler The handler (method) that will be called via reflection
|
* @param handler The handler (method) that will be called via reflection
|
||||||
*/
|
*/
|
||||||
void invoke(Object listener, MethodAccess handler, int methodIndex, Object... message) throws Throwable;
|
void invoke(Object listener, MethodAccess handler, int methodIndex, Object... messages) throws Throwable;
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
|||||||
import dorkbox.util.messagebus.dispatch.IHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.IHandlerInvocation;
|
||||||
import dorkbox.util.messagebus.dispatch.ReflectiveHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.ReflectiveHandlerInvocation;
|
||||||
import dorkbox.util.messagebus.dispatch.SynchronizedHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.SynchronizedHandlerInvocation;
|
||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|
||||||
import org.omg.CORBA.BooleanHolder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -43,9 +41,9 @@ public class Subscription {
|
|||||||
private final IHandlerInvocation invocation;
|
private final IHandlerInvocation invocation;
|
||||||
private final Collection<Object> listeners;
|
private final Collection<Object> listeners;
|
||||||
|
|
||||||
public Subscription(MessageHandler handler) {
|
public Subscription(final MessageHandler handler, final float loadFactor, final int stripeSize) {
|
||||||
this.handlerMetadata = handler;
|
this.handlerMetadata = handler;
|
||||||
this.listeners = new StrongConcurrentSetV8<Object>(16, 0.85F, 15);
|
this.listeners = new StrongConcurrentSetV8<Object>(16, loadFactor, stripeSize);
|
||||||
// this.listeners = new StrongConcurrentSet<Object>(16, 0.85F);
|
// this.listeners = new StrongConcurrentSet<Object>(16, 0.85F);
|
||||||
// this.listeners = new ConcurrentLinkedQueue2<Object>();
|
// this.listeners = new ConcurrentLinkedQueue2<Object>();
|
||||||
// this.listeners = new CopyOnWriteArrayList<Object>();
|
// this.listeners = new CopyOnWriteArrayList<Object>();
|
||||||
@ -62,7 +60,7 @@ public class Subscription {
|
|||||||
return handlerMetadata;
|
return handlerMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?>[] getHandledMessageTypes() {
|
public final Class<?>[] getHandledMessageTypes() {
|
||||||
return this.handlerMetadata.getHandledMessages();
|
return this.handlerMetadata.getHandledMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,13 +88,10 @@ public class Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only used in unit-test
|
// only used in unit-test
|
||||||
public int size() {
|
public final int size() {
|
||||||
return this.listeners.size();
|
return this.listeners.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if there were listeners for this publication, false if there was nothing
|
|
||||||
*/
|
|
||||||
public final void publish(final Object message) throws Throwable {
|
public final void publish(final Object message) throws Throwable {
|
||||||
final MethodAccess handler = this.handlerMetadata.getHandler();
|
final MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
final int handleIndex = this.handlerMetadata.getMethodIndex();
|
final int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
@ -112,126 +107,59 @@ public class Subscription {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public final void publishToSubscription(final Object message1, final Object message2) throws Throwable {
|
||||||
* @return true if there were listeners for this publication, false if there was nothing
|
final MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
*/
|
final int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2) {
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
// StrongConcurrentSet<Object> listeners = this.listeners;
|
|
||||||
//
|
Iterator<Object> iterator;
|
||||||
// if (!listeners.isEmpty()) {
|
Object listener;
|
||||||
// MethodAccess handler = this.handlerMetadata.getHandler();
|
|
||||||
// int handleIndex = this.handlerMetadata.getMethodIndex();
|
for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
||||||
// IHandlerInvocation invocation = this.invocation;
|
listener = iterator.next();
|
||||||
//
|
|
||||||
//
|
invocation.invoke(listener, handler, handleIndex, message1, message2);
|
||||||
// ISetEntry<Object> current = listeners.head;
|
}
|
||||||
// Object listener;
|
|
||||||
// while (current != null) {
|
|
||||||
// listener = current.getValue();
|
|
||||||
// current = current.next();
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// invocation.invoke(listener, handler, handleIndex, message1, message2);
|
|
||||||
// } catch (IllegalAccessException e) {
|
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "The class or method is not accessible")
|
|
||||||
// .setCause(e)
|
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
|
||||||
// .setPublishedObject(message1, message2));
|
|
||||||
// } catch (IllegalArgumentException e) {
|
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "Wrong arguments passed to method. Was: " +
|
|
||||||
// message1.getClass() + ", " +
|
|
||||||
// message2.getClass()
|
|
||||||
// + ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
|
||||||
// handler.getParameterTypes()[1]
|
|
||||||
// )
|
|
||||||
// .setCause(e)
|
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
|
||||||
// .setPublishedObject(message1, message2));
|
|
||||||
// } catch (Throwable e) {
|
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "The Message handler code threw an exception")
|
|
||||||
// .setCause(e)
|
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
|
||||||
// .setPublishedObject(message1, message2));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// booleanHolder.bool = true;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public final void publishToSubscription(final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
* @return true if there were listeners for this publication, false if there was nothing
|
final MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
*/
|
final int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2, Object message3) {
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
// StrongConcurrentSet<Object> listeners = this.listeners;
|
|
||||||
//
|
Iterator<Object> iterator;
|
||||||
// if (!listeners.isEmpty()) {
|
Object listener;
|
||||||
// MethodAccess handler = this.handlerMetadata.getHandler();
|
|
||||||
// int handleIndex = this.handlerMetadata.getMethodIndex();
|
for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
||||||
// IHandlerInvocation invocation = this.invocation;
|
listener = iterator.next();
|
||||||
//
|
|
||||||
//
|
invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
||||||
// ISetEntry<Object> current = listeners.head;
|
}
|
||||||
// Object listener;
|
}
|
||||||
// while (current != null) {
|
|
||||||
// listener = current.getValue();
|
public final void publishToSubscription(final Object... messages) throws Throwable {
|
||||||
// current = current.next();
|
final MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
//
|
final int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
// try {
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
// invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
|
||||||
// } catch (IllegalAccessException e) {
|
Iterator<Object> iterator;
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
Object listener;
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "The class or method is not accessible")
|
for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
||||||
// .setCause(e)
|
listener = iterator.next();
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
invocation.invoke(listener, handler, handleIndex, messages);
|
||||||
// .setPublishedObject(message1, message2, message3));
|
}
|
||||||
// } catch (IllegalArgumentException e) {
|
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "Wrong arguments passed to method. Was: " +
|
|
||||||
// message1.getClass() + ", " +
|
|
||||||
// message2.getClass() + ", " +
|
|
||||||
// message3.getClass()
|
|
||||||
// + ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
|
||||||
// handler.getParameterTypes()[1] + ", " +
|
|
||||||
// handler.getParameterTypes()[2]
|
|
||||||
// )
|
|
||||||
// .setCause(e)
|
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
|
||||||
// .setPublishedObject(message1, message2, message3));
|
|
||||||
// } catch (Throwable e) {
|
|
||||||
// errorHandler.handlePublicationError(new PublicationError()
|
|
||||||
// .setMessage("Error during invocation of message handler. " +
|
|
||||||
// "The Message handler code threw an exception")
|
|
||||||
// .setCause(e)
|
|
||||||
// .setMethodName(handler.getMethodNames()[handleIndex])
|
|
||||||
// .setListener(listener)
|
|
||||||
// .setPublishedObject(message1, message2, message3));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// booleanHolder.bool = true;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public final int hashCode() {
|
||||||
return this.ID;
|
return this.ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public final boolean equals(Object obj) {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -246,17 +174,15 @@ public class Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// inside a write lock
|
// inside a write lock
|
||||||
// also puts it into the correct map if it's not already there
|
// add this subscription to each of the handled types
|
||||||
public Collection<Subscription> createPublicationSubscriptions(final Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle,
|
// to activate this sub for publication
|
||||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti,
|
public void registerForPublication(final Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle,
|
||||||
AtomicBoolean varArgPossibility, SubscriptionUtils utils) {
|
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti,
|
||||||
|
final AtomicBoolean varArgPossibility, final SubscriptionUtils utils) {
|
||||||
|
|
||||||
final Class<?>[] messageHandlerTypes = handlerMetadata.getHandledMessages();
|
final Class<?>[] messageHandlerTypes = handlerMetadata.getHandledMessages();
|
||||||
final int size = messageHandlerTypes.length;
|
final int size = messageHandlerTypes.length;
|
||||||
|
|
||||||
// ConcurrentSet<Subscription> subsPerType;
|
|
||||||
|
|
||||||
// SubscriptionUtils utils = this.utils;
|
|
||||||
Class<?> type0 = messageHandlerTypes[0];
|
Class<?> type0 = messageHandlerTypes[0];
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
@ -269,73 +195,45 @@ public class Subscription {
|
|||||||
if (isArray) {
|
if (isArray) {
|
||||||
varArgPossibility.lazySet(true);
|
varArgPossibility.lazySet(true);
|
||||||
}
|
}
|
||||||
utils.cacheSuperClasses(type0);
|
|
||||||
|
|
||||||
subsPerMessageSingle.put(type0, subs);
|
subsPerMessageSingle.put(type0, subs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return subs;
|
subs.add(this);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
ArrayList<Subscription> subs = subsPerMessageMulti.get(type0, messageHandlerTypes[1]);
|
||||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
if (subs == null) {
|
||||||
// subsPerType = subHolderSingle.publish();
|
subs = new ArrayList<Subscription>();
|
||||||
//
|
|
||||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, type0, types[1]);
|
subsPerMessageMulti.put(subs, type0, messageHandlerTypes[1]);
|
||||||
// if (putIfAbsent != null) {
|
}
|
||||||
// return putIfAbsent;
|
|
||||||
// } else {
|
subs.add(this);
|
||||||
// subHolderSingle.set(subHolderSingle.initialValue());
|
return;
|
||||||
//
|
|
||||||
// // cache the super classes
|
|
||||||
// utils.cacheSuperClasses(type0);
|
|
||||||
// utils.cacheSuperClasses(types[1]);
|
|
||||||
//
|
|
||||||
// return subsPerType;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
ArrayList<Subscription> subs = subsPerMessageMulti.get(type0, messageHandlerTypes[1], messageHandlerTypes[2]);
|
||||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
if (subs == null) {
|
||||||
// subsPerType = subHolderSingle.publish();
|
subs = new ArrayList<Subscription>();
|
||||||
//
|
|
||||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, type0, types[1], types[2]);
|
subsPerMessageMulti.put(subs, type0, messageHandlerTypes[1], messageHandlerTypes[2]);
|
||||||
// if (putIfAbsent != null) {
|
}
|
||||||
// return putIfAbsent;
|
|
||||||
// } else {
|
subs.add(this);
|
||||||
// subHolderSingle.set(subHolderSingle.initialValue());
|
return;
|
||||||
//
|
|
||||||
// // cache the super classes
|
|
||||||
// utils.cacheSuperClasses(type0);
|
|
||||||
// utils.cacheSuperClasses(types[1]);
|
|
||||||
// utils.cacheSuperClasses(types[2]);
|
|
||||||
//
|
|
||||||
// return subsPerType;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
ArrayList<Subscription> subs = subsPerMessageMulti.get(messageHandlerTypes);
|
||||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
if (subs == null) {
|
||||||
// subsPerType = subHolderSingle.publish();
|
subs = new ArrayList<Subscription>();
|
||||||
//
|
|
||||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, types);
|
subsPerMessageMulti.put(subs, messageHandlerTypes);
|
||||||
// if (putIfAbsent != null) {
|
}
|
||||||
// return putIfAbsent;
|
|
||||||
// } else {
|
subs.add(this);
|
||||||
// subHolderSingle.set(subHolderSingle.initialValue());
|
return;
|
||||||
//
|
|
||||||
// Class<?> c;
|
|
||||||
// int length = types.length;
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// c = types[i];
|
|
||||||
//
|
|
||||||
// // cache the super classes
|
|
||||||
// utils.cacheSuperClasses(c);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return subsPerType;
|
|
||||||
// }
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,28 +54,29 @@ public final class SubscriptionManager {
|
|||||||
private final VarArgUtils varArgUtils;
|
private final VarArgUtils varArgUtils;
|
||||||
|
|
||||||
private final StampedLock lock = new StampedLock();
|
private final StampedLock lock = new StampedLock();
|
||||||
|
private final int numberOfThreads;
|
||||||
|
|
||||||
public SubscriptionManager(int numberOfThreads) {
|
public SubscriptionManager(int numberOfThreads) {
|
||||||
float loadFactor = SubscriptionManager.LOAD_FACTOR;
|
this.numberOfThreads = numberOfThreads;
|
||||||
|
|
||||||
// modified ONLY during SUB/UNSUB
|
// modified ONLY during SUB/UNSUB
|
||||||
{
|
{
|
||||||
this.nonListeners = new ConcurrentHashMapV8<Class<?>, Boolean>(4, loadFactor, numberOfThreads);
|
this.nonListeners = new ConcurrentHashMapV8<Class<?>, Boolean>(4, LOAD_FACTOR, numberOfThreads);
|
||||||
|
|
||||||
this.subscriptionsPerMessageSingle = new ConcurrentHashMapV8<Class<?>, ArrayList<Subscription>>(32, LOAD_FACTOR, 1);
|
this.subscriptionsPerMessageSingle = new ConcurrentHashMapV8<Class<?>, ArrayList<Subscription>>(32, LOAD_FACTOR, 1);
|
||||||
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>(4, loadFactor);
|
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>(4, LOAD_FACTOR);
|
||||||
|
|
||||||
// only used during SUB/UNSUB
|
// only used during SUB/UNSUB
|
||||||
this.subscriptionsPerListener = new ConcurrentHashMapV8<Class<?>, Subscription[]>(32, LOAD_FACTOR, 1);
|
this.subscriptionsPerListener = new ConcurrentHashMapV8<Class<?>, Subscription[]>(32, LOAD_FACTOR, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
final SuperClassUtils superClass = new SuperClassUtils(loadFactor, 1);
|
final SuperClassUtils superClass = new SuperClassUtils(LOAD_FACTOR, 1);
|
||||||
this.utils = new SubscriptionUtils(superClass, this.subscriptionsPerMessageSingle, this.subscriptionsPerMessageMulti, loadFactor,
|
this.utils = new SubscriptionUtils(superClass, this.subscriptionsPerMessageSingle, this.subscriptionsPerMessageMulti, LOAD_FACTOR,
|
||||||
numberOfThreads);
|
numberOfThreads);
|
||||||
|
|
||||||
// var arg subscriptions keep track of which subscriptions can handle varArgs. SUB/UNSUB dumps it, so it is recreated dynamically.
|
// var arg subscriptions keep track of which subscriptions can handle varArgs. SUB/UNSUB dumps it, so it is recreated dynamically.
|
||||||
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
||||||
this.varArgUtils = new VarArgUtils(this.utils, superClass, this.subscriptionsPerMessageSingle, loadFactor, numberOfThreads);
|
this.varArgUtils = new VarArgUtils(this.utils, superClass, this.subscriptionsPerMessageSingle, LOAD_FACTOR, numberOfThreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void shutdown() {
|
public final void shutdown() {
|
||||||
@ -126,7 +127,6 @@ public final class SubscriptionManager {
|
|||||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti = this.subscriptionsPerMessageMulti;
|
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti = this.subscriptionsPerMessageMulti;
|
||||||
|
|
||||||
final Subscription[] subsPerListener = new Subscription[handlersSize];
|
final Subscription[] subsPerListener = new Subscription[handlersSize];
|
||||||
Collection<Subscription> subsForPublication;
|
|
||||||
|
|
||||||
// create the subscription
|
// create the subscription
|
||||||
MessageHandler messageHandler;
|
MessageHandler messageHandler;
|
||||||
@ -136,12 +136,15 @@ public final class SubscriptionManager {
|
|||||||
messageHandler = messageHandlers[i];
|
messageHandler = messageHandlers[i];
|
||||||
|
|
||||||
// create the subscription
|
// create the subscription
|
||||||
subscription = new Subscription(messageHandler);
|
subscription = new Subscription(messageHandler, LOAD_FACTOR, numberOfThreads);
|
||||||
subscription.subscribe(listener);
|
subscription.subscribe(listener);
|
||||||
|
|
||||||
subsPerListener[i] = subscription; // activates this sub for sub/unsub
|
subsPerListener[i] = subscription; // activates this sub for sub/unsub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ConcurrentMap<Class<?>, Subscription[]> subsPerListenerMap = this.subscriptionsPerListener;
|
||||||
|
final AtomicBoolean varArgPossibility = this.varArgPossibility;
|
||||||
|
final SubscriptionUtils utils = this.utils;
|
||||||
|
|
||||||
// now write lock for the least expensive part. This is a deferred "double checked lock", but is necessary because
|
// now write lock for the least expensive part. This is a deferred "double checked lock", but is necessary because
|
||||||
// of the huge number of reads compared to writes.
|
// of the huge number of reads compared to writes.
|
||||||
@ -149,23 +152,16 @@ public final class SubscriptionManager {
|
|||||||
final StampedLock lock = this.lock;
|
final StampedLock lock = this.lock;
|
||||||
final long stamp = lock.writeLock();
|
final long stamp = lock.writeLock();
|
||||||
|
|
||||||
final ConcurrentMap<Class<?>, Subscription[]> subsPerListenerMap = this.subscriptionsPerListener;
|
|
||||||
subscriptions = subsPerListenerMap.get(listenerClass);
|
subscriptions = subsPerListenerMap.get(listenerClass);
|
||||||
|
|
||||||
// it was still null, so we actually have to create the rest of the subs
|
// it was still null, so we actually have to create the rest of the subs
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
final AtomicBoolean varArgPossibility = this.varArgPossibility;
|
|
||||||
final SubscriptionUtils utils = this.utils;
|
|
||||||
|
|
||||||
for (int i = 0; i < handlersSize; i++) {
|
for (int i = 0; i < handlersSize; i++) {
|
||||||
subscription = subsPerListener[i];
|
subscription = subsPerListener[i];
|
||||||
|
|
||||||
// now add this subscription to each of the handled types
|
// now add this subscription to each of the handled types
|
||||||
subsForPublication = subscription
|
// to activate this sub for publication
|
||||||
.createPublicationSubscriptions(subsPerMessageSingle, subsPerMessageMulti, varArgPossibility, utils);
|
subscription.registerForPublication(subsPerMessageSingle, subsPerMessageMulti, varArgPossibility, utils);
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
subsForPublication.add(subscription); // activates this sub for publication
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subsPerListenerMap.put(listenerClass, subsPerListener);
|
subsPerListenerMap.put(listenerClass, subsPerListener);
|
||||||
@ -182,7 +178,6 @@ public final class SubscriptionManager {
|
|||||||
// subscriptions already exist and must only be updated
|
// subscriptions already exist and must only be updated
|
||||||
// only publish here if our single-check was OK, or our double-check was OK
|
// only publish here if our single-check was OK, or our double-check was OK
|
||||||
Subscription subscription;
|
Subscription subscription;
|
||||||
|
|
||||||
for (int i = 0; i < subscriptions.length; i++) {
|
for (int i = 0; i < subscriptions.length; i++) {
|
||||||
subscription = subscriptions[i];
|
subscription = subscriptions[i];
|
||||||
subscription.subscribe(listener);
|
subscription.subscribe(listener);
|
||||||
|
@ -1,16 +1,5 @@
|
|||||||
package dorkbox.util.messagebus;
|
package dorkbox.util.messagebus;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.LinkedTransferQueue;
|
|
||||||
|
|
||||||
import dorkbox.util.messagebus.annotations.Handler;
|
import dorkbox.util.messagebus.annotations.Handler;
|
||||||
import dorkbox.util.messagebus.common.ConcurrentHashMapV8;
|
import dorkbox.util.messagebus.common.ConcurrentHashMapV8;
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
@ -20,6 +9,11 @@ import dorkbox.util.messagebus.common.thread.ConcurrentLinkedQueue2;
|
|||||||
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
import dorkbox.util.messagebus.subscription.Subscription;
|
import dorkbox.util.messagebus.subscription.Subscription;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
|
|
||||||
|
|
||||||
public class PerfTest_Collections {
|
public class PerfTest_Collections {
|
||||||
public static final int REPETITIONS = 10 * 1000 * 100;
|
public static final int REPETITIONS = 10 * 1000 * 100;
|
||||||
@ -75,7 +69,7 @@ public class PerfTest_Collections {
|
|||||||
|
|
||||||
for (int i=0;i<size;i++) {
|
for (int i=0;i<size;i++) {
|
||||||
for (MessageHandler x : allHandlers) {
|
for (MessageHandler x : allHandlers) {
|
||||||
set.add(new Subscription(x));
|
set.add(new Subscription(x, .85F, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user