WIP var-arg/super-var-arg implementation
This commit is contained in:
parent
67639ffb48
commit
30617a2b57
|
@ -14,7 +14,7 @@ import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|||
* <p/>
|
||||
* Each message publication is isolated from all other running publications such that it does not interfere with them.
|
||||
* Hence, the bus generally expects message handlers to be stateless as it may invoke them concurrently if multiple
|
||||
* messages getSubscriptions published asynchronously. If handlers are stateful and not thread-safe they can be marked to be invoked
|
||||
* messages publish published asynchronously. If handlers are stateful and not thread-safe they can be marked to be invoked
|
||||
* in a synchronized fashion using @Synchronized annotation
|
||||
*
|
||||
* <p/>
|
||||
|
@ -25,7 +25,7 @@ import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|||
* <p/>
|
||||
* By default, the bus uses weak references to all listeners such that registered listeners do not need to
|
||||
* be explicitly unregistered to be eligible for garbage collection. Dead (garbage collected) listeners are
|
||||
* removed on-the-fly as messages getSubscriptions dispatched. This can be changed using the @Listener annotation.
|
||||
* removed on-the-fly as messages publish dispatched. This can be changed using the @Listener annotation.
|
||||
*
|
||||
* <p/>
|
||||
* Generally message handlers will be invoked in inverse sequence of subscription but any
|
||||
|
@ -50,7 +50,7 @@ import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|||
*
|
||||
* <p/>
|
||||
* NOTE: Generic type parameters of messages will not be taken into account, e.g. a List<Long> will
|
||||
* getSubscriptions dispatched to all message handlers that take an instance of List as their parameter
|
||||
* publish dispatched to all message handlers that take an instance of List as their parameter
|
||||
*
|
||||
* @Author bennidi
|
||||
* Date: 2/8/12
|
||||
|
|
|
@ -73,21 +73,20 @@ public class MultiMBassador implements IMessageBus {
|
|||
if (forceExactMatches) {
|
||||
subscriptionMatcher = new Matcher() {
|
||||
@Override
|
||||
public Subscription[] getSubscriptions(Class<?> messageClass) {
|
||||
return subscriptionManager.getSubscriptionsForcedExact(messageClass);
|
||||
public boolean publish(Object message) throws Throwable {
|
||||
return subscriptionManager.publishExact(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
subscriptionMatcher = new Matcher() {
|
||||
@Override
|
||||
public Subscription[] getSubscriptions(Class<?> messageClass) {
|
||||
return subscriptionManager.getSubscriptions(messageClass);
|
||||
public boolean publish(Object message) throws Throwable {
|
||||
return subscriptionManager.publish(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
this.threads = new ArrayDeque<Thread>(numberOfThreads);
|
||||
|
||||
NamedThreadFactory dispatchThreadFactory = new NamedThreadFactory("MessageBus");
|
||||
|
@ -101,6 +100,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
MultiNode node = new MultiNode();
|
||||
while (!MultiMBassador.this.shuttingDown) {
|
||||
try {
|
||||
//noinspection InfiniteLoopStatement
|
||||
while (true) {
|
||||
IN_QUEUE.take(node);
|
||||
switch (node.messageType) {
|
||||
|
@ -112,7 +112,6 @@ public class MultiMBassador implements IMessageBus {
|
|||
continue;
|
||||
case 3:
|
||||
publish(node.item1, node.item2, node.item3);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -120,22 +119,21 @@ public class MultiMBassador implements IMessageBus {
|
|||
switch (node.messageType) {
|
||||
case 1: {
|
||||
handlePublicationError(
|
||||
new PublicationError().setMessage("Thread interupted while processing message")
|
||||
new PublicationError().setMessage("Thread interrupted while processing message")
|
||||
.setCause(e).setPublishedObject(node.item1));
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
handlePublicationError(
|
||||
new PublicationError().setMessage("Thread interupted while processing message")
|
||||
new PublicationError().setMessage("Thread interrupted while processing message")
|
||||
.setCause(e).setPublishedObject(node.item1, node.item2));
|
||||
continue;
|
||||
}
|
||||
case 3: {
|
||||
handlePublicationError(
|
||||
new PublicationError().setMessage("Thread interupted while processing message")
|
||||
new PublicationError().setMessage("Thread interrupted while processing message")
|
||||
.setCause(e)
|
||||
.setPublishedObject(node.item1, node.item2, node.item3));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,97 +200,19 @@ public class MultiMBassador implements IMessageBus {
|
|||
return this.dispatchQueue.hasPendingMessages();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void publish(final Object message) {
|
||||
try {
|
||||
boolean subsPublished = false;
|
||||
final SubscriptionManager manager = this.subscriptionManager;
|
||||
final Class<?> messageClass = message.getClass();
|
||||
boolean subsPublished = subscriptionMatcher.publish(message);
|
||||
|
||||
Subscription[] subscriptions;
|
||||
Subscription sub;
|
||||
|
||||
subscriptions = subscriptionMatcher.getSubscriptions(messageClass);
|
||||
int c = 0;
|
||||
// Run subscriptions
|
||||
if (subscriptions != null) {
|
||||
subsPublished = true;
|
||||
|
||||
for (int i = 0; i < subscriptions.length; i++) {
|
||||
sub = subscriptions[i];
|
||||
sub.publish(message);
|
||||
c = sub.c();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if (!this.forceExactMatches) {
|
||||
// Subscription[] superSubscriptions = manager.getSuperSubscriptions(messageClass); // NOT return null
|
||||
// // now getSubscriptions superClasses
|
||||
// int length = superSubscriptions.length;
|
||||
//
|
||||
// if (length > 0) {
|
||||
// for (int i=0;i<length;i++) {
|
||||
// sub = superSubscriptions[i];
|
||||
//
|
||||
// sub.publish(message);
|
||||
// }
|
||||
//
|
||||
// subsPublished = true;
|
||||
// }
|
||||
|
||||
|
||||
// if (superSubscriptions != null && !superSubscriptions.isEmpty()) {
|
||||
// for (iterator = superSubscriptions.iterator(); iterator.hasNext();) {
|
||||
// sub = iterator.next();
|
||||
//
|
||||
// // this catches all exception types
|
||||
// sub.publishToSubscription(this, message);
|
||||
// }
|
||||
// subsPublished = true;
|
||||
// }
|
||||
|
||||
|
||||
// // publish to var arg, only if not already an array
|
||||
// if (manager.hasVarArgPossibility() && !manager.utils.isArray(messageClass)) {
|
||||
// Object[] asArray = null;
|
||||
//
|
||||
// ConcurrentSet<Subscription> varargSubscriptions = manager.getVarArgSubscriptions(messageClass);
|
||||
// if (varargSubscriptions != null && !varargSubscriptions.isEmpty()) {
|
||||
// asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||
// asArray[0] = message;
|
||||
//
|
||||
// for (iterator = varargSubscriptions.iterator(); iterator.hasNext();) {
|
||||
// sub = iterator.next();
|
||||
// // this catches all exception types
|
||||
// sub.publishToSubscription(this, subsPublished, asArray);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ConcurrentSet<Subscription> varargSuperSubscriptions = manager.getVarArgSuperSubscriptions(messageClass);
|
||||
// // now getSubscriptions array based superClasses (but only if those ALSO accept vararg)
|
||||
// if (varargSuperSubscriptions != null && !varargSuperSubscriptions.isEmpty()) {
|
||||
// if (asArray == null) {
|
||||
// asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||
// asArray[0] = message;
|
||||
// }
|
||||
// for (iterator = varargSuperSubscriptions.iterator(); iterator.hasNext();) {
|
||||
// sub = iterator.next();
|
||||
// // this catches all exception types
|
||||
// sub.publishToSubscription(this, subsPublished, asArray);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (c == 0 && !subsPublished) {
|
||||
if (!subsPublished) {
|
||||
// Dead Event must EXACTLY MATCH (no subclasses)
|
||||
Subscription[] deadSubscriptions = manager.getSubscriptionsForcedExact(DeadMessage.class);
|
||||
Subscription[] deadSubscriptions = subscriptionManager.getSubscriptionsForcedExact(DeadMessage.class);
|
||||
if (deadSubscriptions != null) {
|
||||
DeadMessage deadMessage = new DeadMessage(message);
|
||||
|
||||
Subscription sub;
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < deadSubscriptions.length; i++) {
|
||||
sub = deadSubscriptions[i];
|
||||
|
||||
|
@ -314,7 +234,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// Class<?> messageClass2 = message2.getClass();
|
||||
//
|
||||
// StrongConcurrentSet<Subscription> subscriptions = manager.getSubscriptionsByMessageType(messageClass1, messageClass2);
|
||||
// BooleanHolder subsPublished = this.booleanThreadLocal.getSubscriptions();
|
||||
// BooleanHolder subsPublished = this.booleanThreadLocal.publish();
|
||||
// subsPublished.bool = false;
|
||||
//
|
||||
// ISetEntry<Subscription> current;
|
||||
|
@ -334,7 +254,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
//
|
||||
// if (!this.forceExactMatches) {
|
||||
// StrongConcurrentSet<Subscription> superSubscriptions = manager.getSuperSubscriptions(messageClass1, messageClass2);
|
||||
// // now getSubscriptions superClasses
|
||||
// // now publish superClasses
|
||||
// if (superSubscriptions != null) {
|
||||
// current = superSubscriptions.head;
|
||||
// while (current != null) {
|
||||
|
@ -368,7 +288,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// }
|
||||
//
|
||||
// StrongConcurrentSet<Subscription> varargSuperSubscriptions = manager.getVarArgSuperSubscriptions(messageClass1);
|
||||
// // now getSubscriptions array based superClasses (but only if those ALSO accept vararg)
|
||||
// // now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
// if (varargSuperSubscriptions != null && !varargSuperSubscriptions.isEmpty()) {
|
||||
// if (asArray == null) {
|
||||
// asArray = (Object[]) Array.newInstance(messageClass1, 2);
|
||||
|
@ -388,7 +308,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// } else {
|
||||
// StrongConcurrentSet<Subscription> varargSuperMultiSubscriptions = manager.getVarArgSuperSubscriptions(messageClass1, messageClass2);
|
||||
//
|
||||
// // now getSubscriptions array based superClasses (but only if those ALSO accept vararg)
|
||||
// // now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
// if (varargSuperMultiSubscriptions != null && !varargSuperMultiSubscriptions.isEmpty()) {
|
||||
// current = varargSuperMultiSubscriptions.head;
|
||||
// while (current != null) {
|
||||
|
@ -438,7 +358,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// Class<?> messageClass3 = message3.getClass();
|
||||
//
|
||||
// StrongConcurrentSet<Subscription> subscriptions = manager.getSubscriptionsByMessageType(messageClass1, messageClass2, messageClass3);
|
||||
// BooleanHolder subsPublished = this.booleanThreadLocal.getSubscriptions();
|
||||
// BooleanHolder subsPublished = this.booleanThreadLocal.publish();
|
||||
// subsPublished.bool = false;
|
||||
//
|
||||
// ISetEntry<Subscription> current;
|
||||
|
@ -459,7 +379,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
//
|
||||
// if (!this.forceExactMatches) {
|
||||
// StrongConcurrentSet<Subscription> superSubscriptions = manager.getSuperSubscriptions(messageClass1, messageClass2, messageClass3);
|
||||
// // now getSubscriptions superClasses
|
||||
// // now publish superClasses
|
||||
// if (superSubscriptions != null) {
|
||||
// current = superSubscriptions.head;
|
||||
// while (current != null) {
|
||||
|
@ -493,7 +413,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// }
|
||||
//
|
||||
// StrongConcurrentSet<Subscription> varargSuperSubscriptions = manager.getVarArgSuperSubscriptions(messageClass1);
|
||||
// // now getSubscriptions array based superClasses (but only if those ALSO accept vararg)
|
||||
// // now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
// if (varargSuperSubscriptions != null && !varargSuperSubscriptions.isEmpty()) {
|
||||
// if (asArray == null) {
|
||||
// asArray = (Object[]) Array.newInstance(messageClass1, 3);
|
||||
|
@ -514,7 +434,7 @@ public class MultiMBassador implements IMessageBus {
|
|||
// } else {
|
||||
// StrongConcurrentSet<Subscription> varargSuperMultiSubscriptions = manager.getVarArgSuperSubscriptions(messageClass1, messageClass2, messageClass3);
|
||||
//
|
||||
// // now getSubscriptions array based superClasses (but only if those ALSO accept vararg)
|
||||
// // now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
// if (varargSuperMultiSubscriptions != null && !varargSuperMultiSubscriptions.isEmpty()) {
|
||||
// current = varargSuperMultiSubscriptions.head;
|
||||
// while (current != null) {
|
||||
|
|
|
@ -27,14 +27,14 @@ import java.util.concurrent.ForkJoinPool;
|
|||
* interoperable with {@code Hashtable} in programs that rely on its
|
||||
* thread safety but not on its synchronization details.
|
||||
*
|
||||
* <p>Retrieval operations (including {@code getSubscriptions}) generally do not
|
||||
* <p>Retrieval operations (including {@code get}) generally do not
|
||||
* block, so may overlap with update operations (including {@code put}
|
||||
* and {@code remove}). Retrievals reflect the results of the most
|
||||
* recently <em>completed</em> update operations holding upon their
|
||||
* onset. (More formally, an update operation for a given key bears a
|
||||
* <em>happens-before</em> relation with any (non-null) retrieval for
|
||||
* that key reporting the updated value.) For aggregate operations
|
||||
* such as {@code putAll} and {@code shutdown}, concurrent retrievals may
|
||||
* such as {@code putAll} and {@code clear}, concurrent retrievals may
|
||||
* reflect insertion or removal of only some entries. Similarly,
|
||||
* Iterators and Enumerations return elements reflecting the state of
|
||||
* the hash table at some point at or since the creation of the
|
||||
|
@ -141,7 +141,7 @@ import java.util.concurrent.ForkJoinPool;
|
|||
*
|
||||
* <p>The concurrency properties of bulk operations follow
|
||||
* from those of ConcurrentHashMapV8: Any non-null result returned
|
||||
* from {@code getSubscriptions(key)} and related access methods bears a
|
||||
* from {@code get(key)} and related access methods bears a
|
||||
* happens-before relation with the associated insertion or
|
||||
* update. The result of any bulk operation reflects the
|
||||
* composition of these per-element relations (but is not
|
||||
|
@ -275,7 +275,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// * Overview:
|
||||
// *
|
||||
// * The primary design goal of this hash table is to maintain
|
||||
// * concurrent readability (typically method getSubscriptions(), but also
|
||||
// * concurrent readability (typically method publish(), but also
|
||||
// * iterators and related methods) while minimizing update
|
||||
// * contention. Secondary goals are to keep space consumption about
|
||||
// * the same or better than java.util.HashMap, and to support high
|
||||
|
@ -632,7 +632,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// }
|
||||
//
|
||||
// /**
|
||||
// * Virtualized support for map.getSubscriptions(); overridden in subclasses.
|
||||
// * Virtualized support for map.publish(); overridden in subclasses.
|
||||
// */
|
||||
// Node<K,V> find(int h, Object k) {
|
||||
// Node<K,V> e = this;
|
||||
|
@ -931,7 +931,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// * @throws NullPointerException if the specified key is null
|
||||
// */
|
||||
// @Override
|
||||
// public V getSubscriptions(Object key) {
|
||||
// public V publish(Object key) {
|
||||
// Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
|
||||
// int h = spread(key.hashCode());
|
||||
// if ((tab = this.table) != null && (n = tab.length) > 0 &&
|
||||
|
@ -965,7 +965,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// */
|
||||
// @Override
|
||||
// public boolean containsKey(Object key) {
|
||||
// return getSubscriptions(key) != null;
|
||||
// return publish(key) != null;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
|
@ -1000,7 +1000,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// * Maps the specified key to the specified value in this table.
|
||||
// * Neither the key nor the value can be null.
|
||||
// *
|
||||
// * <p>The value can be retrieved by calling the {@code getSubscriptions} method
|
||||
// * <p>The value can be retrieved by calling the {@code publish} method
|
||||
// * with a key that is equal to the original key.
|
||||
// *
|
||||
// * @param key key with which the specified value is to be associated
|
||||
|
@ -1381,7 +1381,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// Traverser<K,V> it = new Traverser<K,V>(t, f, 0, f);
|
||||
// for (Node<K,V> p; (p = it.advance()) != null; ) {
|
||||
// V val = p.val;
|
||||
// Object v = m.getSubscriptions(p.key);
|
||||
// Object v = m.publish(p.key);
|
||||
// if (v == null || v != val && !v.equals(val)) {
|
||||
// return false;
|
||||
// }
|
||||
|
@ -1390,7 +1390,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// Object mk, mv, v;
|
||||
// if ((mk = e.getKey()) == null ||
|
||||
// (mv = e.getValue()) == null ||
|
||||
// (v = getSubscriptions(mk)) == null ||
|
||||
// (v = publish(mk)) == null ||
|
||||
// mv != v && !mv.equals(v)) {
|
||||
// return false;
|
||||
// }
|
||||
|
@ -1628,7 +1628,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// */
|
||||
// public V getOrDefault(Object key, V defaultValue) {
|
||||
// V v;
|
||||
// return (v = getSubscriptions(key)) == null ? defaultValue : v;
|
||||
// return (v = publish(key)) == null ? defaultValue : v;
|
||||
// }
|
||||
//
|
||||
// public void forEach(BiAction<? super K, ? super V> action) {
|
||||
|
@ -1659,7 +1659,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// throw new NullPointerException();
|
||||
// }
|
||||
// if (replaceNode(key, newValue, oldValue) != null ||
|
||||
// (oldValue = getSubscriptions(key)) == null) {
|
||||
// (oldValue = publish(key)) == null) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
@ -2328,7 +2328,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// !U.compareAndSwapLong(this, BASECOUNT, b = this.baseCount, s = b + x)) {
|
||||
// CounterHashCode hc; CounterCell a; long v; int m;
|
||||
// boolean uncontended = true;
|
||||
// if ((hc = threadCounterHashCode.getSubscriptions()) == null ||
|
||||
// if ((hc = threadCounterHashCode.publish()) == null ||
|
||||
// as == null || (m = as.length - 1) < 0 ||
|
||||
// (a = as[m & hc.code]) == null ||
|
||||
// !(uncontended =
|
||||
|
@ -4037,7 +4037,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// Object k, v, r; Map.Entry<?,?> e;
|
||||
// return o instanceof Map.Entry &&
|
||||
// (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
|
||||
// (r = this.map.getSubscriptions(k)) != null &&
|
||||
// (r = this.map.publish(k)) != null &&
|
||||
// (v = e.getValue()) != null &&
|
||||
// (v == r || v.equals(r));
|
||||
// }
|
||||
|
@ -4328,7 +4328,7 @@ public class ConcurrentHashMapV8<K, V> extends ConcurrentHashMap<K, V>
|
|||
// Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
|
||||
// for (java.lang.reflect.Field f : k.getDeclaredFields()) {
|
||||
// f.setAccessible(true);
|
||||
// Object x = f.getSubscriptions(null);
|
||||
// Object x = f.publish(null);
|
||||
// if (k.isInstance(x)) {
|
||||
// return k.cast(x);
|
||||
// }
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.util.concurrent.locks.Lock;
|
|||
|
||||
|
||||
/**
|
||||
* Simple tree structure that is a map that contains a chain of keys to getSubscriptions to a value.
|
||||
* Simple tree structure that is a map that contains a chain of keys to publish to a value.
|
||||
* <p>
|
||||
* THREAD SAFE, each level in the tree has it's own write lock, and there a tree-global read lock, to prevent writes
|
||||
*
|
||||
|
@ -506,7 +506,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
HashMapTree<KEY, VALUE> objectTree = null;
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
objectTree = getLeaf_NL(key); // protected by lock
|
||||
|
||||
if (objectTree == null) {
|
||||
|
@ -525,7 +525,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
HashMapTree<KEY, VALUE> tree = null;
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(key1); // protected by lock
|
||||
if (tree != null) {
|
||||
tree = tree.getLeaf_NL(key2); // protected by lock
|
||||
|
@ -547,7 +547,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
HashMapTree<KEY, VALUE> tree = null;
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(key1);
|
||||
if (tree != null) {
|
||||
tree = tree.getLeaf_NL(key2);
|
||||
|
@ -573,7 +573,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
HashMapTree<KEY, VALUE> tree = null;
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(keys[0]);
|
||||
|
||||
int size = keys.length;
|
||||
|
@ -625,7 +625,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
Lock READ = this.lock.readLock();
|
||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(key1);
|
||||
if (tree != null) {
|
||||
tree = tree.getLeaf_NL(key2);
|
||||
|
@ -642,7 +642,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
Lock READ = this.lock.readLock();
|
||||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(key1);
|
||||
if (tree != null) {
|
||||
tree = tree.getLeaf_NL(key2);
|
||||
|
@ -668,7 +668,7 @@ public class HashMapTree<KEY, VALUE> {
|
|||
READ.lock(); // allows other readers, blocks others from acquiring update or write locks
|
||||
|
||||
HashMapTree<KEY, VALUE> tree = null;
|
||||
// getSubscriptions value from our children
|
||||
// publish value from our children
|
||||
tree = getLeaf_NL(keys[0]);
|
||||
|
||||
for (int i=1;i<size;i++) {
|
||||
|
|
|
@ -29,11 +29,11 @@ import java.util.Arrays;
|
|||
*/
|
||||
public class MessageHandler {
|
||||
|
||||
// getSubscriptions all listeners defined by the given class (includes
|
||||
// publish all listeners defined by the given class (includes
|
||||
// listeners defined in super classes)
|
||||
public static final MessageHandler[] get(final Class<?> target) {
|
||||
|
||||
// getSubscriptions all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
||||
// publish all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
||||
final Method[] allMethods = ReflectionUtils.getMethods(target);
|
||||
final int length = allMethods.length;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public class SuperClassUtils {
|
|||
Class<?>[] classes = local.get(clazz);
|
||||
|
||||
if (classes == null) {
|
||||
// getSubscriptions all super types of class
|
||||
// publish all super types of class
|
||||
final Class<?>[] superTypes = ReflectionUtils.getSuperTypes(clazz);
|
||||
final int length = superTypes.length;
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package dorkbox.util.messagebus.common;
|
||||
|
||||
import org.jctools.util.UnsafeAccess;
|
||||
|
||||
abstract class VarArgPossibility_P0 {
|
||||
// pre-padding
|
||||
volatile long y0, y1, y2, y4, y5, y6 = 7L;
|
||||
}
|
||||
|
||||
abstract class VarArgPossibility_I0 extends VarArgPossibility_P0 {
|
||||
public boolean hasVarArgPossibility = false;
|
||||
}
|
||||
|
||||
public class VarArgPossibility extends VarArgPossibility_I0 {
|
||||
private static final long BOOL;
|
||||
|
||||
static {
|
||||
try {
|
||||
BOOL = UnsafeAccess.UNSAFE.objectFieldOffset(VarArgPossibility.class.getField("hasVarArgPossibility"));
|
||||
|
||||
// now make sure we can access UNSAFE
|
||||
VarArgPossibility bool = new VarArgPossibility();
|
||||
boolean o = true;
|
||||
bool.set( o);
|
||||
boolean lpItem1 = bool.get();
|
||||
bool.set(false);
|
||||
|
||||
if (lpItem1 != o) {
|
||||
throw new Exception("Cannot access unsafe fields");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public final void set(boolean bool) {
|
||||
UnsafeAccess.UNSAFE.putBoolean(this, BOOL, bool);
|
||||
}
|
||||
|
||||
public final boolean get() {
|
||||
return UnsafeAccess.UNSAFE.getBoolean(this, BOOL);
|
||||
}
|
||||
|
||||
// post-padding
|
||||
volatile long z0, z1, z2, z4, z5, z6 = 7L;
|
||||
|
||||
public VarArgPossibility() {
|
||||
}
|
||||
}
|
|
@ -6,12 +6,12 @@ import dorkbox.util.messagebus.subscription.Subscription;
|
|||
import dorkbox.util.messagebus.subscription.SubscriptionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class VarArgUtils {
|
||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> varArgSubscriptions;
|
||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> varArgSuperClassSubscriptions;
|
||||
private final Map<Class<?>, ArrayList<Subscription>> varArgSubscriptions;
|
||||
private final Map<Class<?>, List<Subscription>> varArgSuperClassSubscriptions;
|
||||
private final HashMapTree<Class<?>, ConcurrentSet<Subscription>> varArgSuperClassSubscriptionsMulti;
|
||||
|
||||
private final SubscriptionHolder subHolderConcurrent;
|
||||
|
@ -20,19 +20,22 @@ public class VarArgUtils {
|
|||
private final int stripeSize;
|
||||
|
||||
private final SubscriptionUtils utils;
|
||||
private final SuperClassUtils superClassUtils;
|
||||
private final Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle;
|
||||
|
||||
|
||||
public VarArgUtils(SubscriptionUtils utils, Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle, float loadFactor,
|
||||
public VarArgUtils(SubscriptionUtils utils, SuperClassUtils superClassUtils,
|
||||
Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle, float loadFactor,
|
||||
int stripeSize) {
|
||||
|
||||
this.utils = utils;
|
||||
this.superClassUtils = superClassUtils;
|
||||
this.subscriptionsPerMessageSingle = subscriptionsPerMessageSingle;
|
||||
this.loadFactor = loadFactor;
|
||||
this.stripeSize = stripeSize;
|
||||
|
||||
this.varArgSubscriptions = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(16, loadFactor, stripeSize);
|
||||
this.varArgSuperClassSubscriptions = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(16, loadFactor, stripeSize);
|
||||
this.varArgSubscriptions = new ConcurrentHashMapV8<Class<?>, ArrayList<Subscription>>(16, loadFactor, stripeSize);
|
||||
this.varArgSuperClassSubscriptions = new ConcurrentHashMapV8<Class<?>, List<Subscription>>(16, loadFactor, stripeSize);
|
||||
this.varArgSuperClassSubscriptionsMulti = new HashMapTree<Class<?>, ConcurrentSet<Subscription>>(4, loadFactor);
|
||||
|
||||
this.subHolderConcurrent = new SubscriptionHolder();
|
||||
|
@ -49,12 +52,38 @@ public class VarArgUtils {
|
|||
// CAN NOT RETURN NULL
|
||||
// check to see if the messageType can convert/publish to the "array" version, without the hit to JNI
|
||||
// and then, returns the array'd version subscriptions
|
||||
public ConcurrentSet<Subscription> getVarArgSubscriptions(Class<?> messageClass) {
|
||||
// ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> local = this.varArgSubscriptions;
|
||||
//
|
||||
// // whenever our subscriptions change, this map is cleared.
|
||||
public ArrayList<Subscription> getVarArgSubscriptions(Class<?> messageClass) {
|
||||
Map<Class<?>, ArrayList<Subscription>> local = this.varArgSubscriptions;
|
||||
|
||||
ArrayList<Subscription> varArgSubs = local.get(messageClass);
|
||||
|
||||
if (varArgSubs == null) {
|
||||
// this gets (and caches) our array type. This is never cleared.
|
||||
final Class<?> arrayVersion = this.superClassUtils.getArrayClass(messageClass);
|
||||
|
||||
ArrayList<Subscription> subs = this.subscriptionsPerMessageSingle.get(arrayVersion);
|
||||
if (subs != null) {
|
||||
int length = subs.size();
|
||||
varArgSubs = new ArrayList<Subscription>(length);
|
||||
|
||||
Subscription sub;
|
||||
for (int i = 0; i < length; i++) {
|
||||
sub = subs.get(i);
|
||||
|
||||
if (sub.acceptsVarArgs()) {
|
||||
varArgSubs.add(sub);
|
||||
}
|
||||
}
|
||||
|
||||
local.put(messageClass, varArgSubs);
|
||||
}
|
||||
}
|
||||
|
||||
return varArgSubs;
|
||||
|
||||
// whenever our subscriptions change, this map is cleared.
|
||||
// SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||
// ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.getSubscriptions();
|
||||
// ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.publish();
|
||||
//
|
||||
// // cache our subscriptions for super classes, so that their access can be fast!
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(messageClass, subsPerType);
|
||||
|
@ -62,13 +91,11 @@ public class VarArgUtils {
|
|||
// // we are the first one in the map
|
||||
// subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
||||
//
|
||||
// // this caches our array type. This is never cleared.
|
||||
// Class<?> arrayVersion = this.utils.getArrayClass(messageClass);
|
||||
//
|
||||
// Iterator<Subscription> iterator;
|
||||
// Subscription sub;
|
||||
//
|
||||
// Collection<Subscription> subs = this.subscriptionsPerMessageSingle.getSubscriptions(arrayVersion);
|
||||
// Collection<Subscription> subs = this.subscriptionsPerMessageSingle.publish(arrayVersion);
|
||||
// if (subs != null) {
|
||||
// for (iterator = subs.iterator(); iterator.hasNext();) {
|
||||
// sub = iterator.next();
|
||||
|
@ -83,7 +110,7 @@ public class VarArgUtils {
|
|||
// return putIfAbsent;
|
||||
// }
|
||||
|
||||
return null;
|
||||
// return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,7 +123,7 @@ public class VarArgUtils {
|
|||
// ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> local = this.varArgSuperClassSubscriptions;
|
||||
//
|
||||
// SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||
// ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.getSubscriptions();
|
||||
// ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.publish();
|
||||
//
|
||||
// // cache our subscriptions for super classes, so that their access can be fast!
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(messageClass, subsPerType);
|
||||
|
@ -123,7 +150,7 @@ public class VarArgUtils {
|
|||
// for (iterator = types.iterator(); iterator.hasNext();) {
|
||||
// superClass = iterator.next();
|
||||
//
|
||||
// Collection<Subscription> subs = local2.getSubscriptions(superClass);
|
||||
// Collection<Subscription> subs = local2.publish(superClass);
|
||||
// if (subs != null) {
|
||||
// for (subIterator = subs.iterator(); subIterator.hasNext();) {
|
||||
// sub = subIterator.next();
|
||||
|
@ -159,7 +186,7 @@ public class VarArgUtils {
|
|||
// subsPerType = subsPerTypeLeaf.getValue();
|
||||
// } else {
|
||||
// SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||
// subsPerType = subHolderConcurrent.getSubscriptions();
|
||||
// subsPerType = subHolderConcurrent.publish();
|
||||
//
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, messageClass1, messageClass2);
|
||||
// if (putIfAbsent != null) {
|
||||
|
@ -207,7 +234,7 @@ public class VarArgUtils {
|
|||
// subsPerType = subsPerTypeLeaf.getValue();
|
||||
// } else {
|
||||
// SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||
// subsPerType = subHolderConcurrent.getSubscriptions();
|
||||
// subsPerType = subHolderConcurrent.publish();
|
||||
//
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, messageClass1, messageClass2, messageClass3);
|
||||
// if (putIfAbsent != null) {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package dorkbox.util.messagebus.common;
|
||||
|
||||
public abstract class item<T> {
|
||||
public volatile Entry<T> head; // reference to the first element
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package dorkbox.util.messagebus.common;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
abstract class item3<T> implements Iterator<T> {
|
||||
public ISetEntry<T> current;
|
||||
|
||||
public item3(ISetEntry<T> current) {
|
||||
this.current = current;
|
||||
}
|
||||
}
|
|
@ -432,7 +432,7 @@ public final class MpmcMultiTransferArrayQueue extends MpmcArrayQueue<Object> {
|
|||
// Successful CAS: full barrier
|
||||
|
||||
final Thread myThread = Thread.currentThread();
|
||||
// final Object node = nodeThreadLocal.getSubscriptions();
|
||||
// final Object node = nodeThreadLocal.publish();
|
||||
|
||||
spType(node, TYPE_CONSUMER);
|
||||
spThread(node, myThread);
|
||||
|
|
|
@ -39,7 +39,7 @@ public class ConcurrentSet<T> extends ConcurrentLinkedQueue2<T> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// had to modify the super implementation so we getSubscriptions Node<T> back
|
||||
// had to modify the super implementation so we publish Node<T> back
|
||||
Node<T> alreadyPresent = this.entries.putIfAbsent(element, this.IN_PROGRESS_MARKER);
|
||||
if (alreadyPresent == null) {
|
||||
// this doesn't already exist
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package dorkbox.util.messagebus.subscription;
|
||||
|
||||
public interface Matcher {
|
||||
Subscription[] getSubscriptions(Class<?> messageClass);
|
||||
boolean publish(Object messageClass) throws Throwable;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
* Date: 2/2/15
|
||||
*/
|
||||
public class Subscription {
|
||||
private static AtomicInteger ID_COUNTER = new AtomicInteger();
|
||||
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
||||
public final int ID = ID_COUNTER.getAndIncrement();
|
||||
|
||||
|
||||
|
@ -86,11 +86,6 @@ public class Subscription {
|
|||
return this.listeners.size();
|
||||
}
|
||||
|
||||
int c = 0;
|
||||
public int c() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there were listeners for this publication, false if there was nothing
|
||||
*/
|
||||
|
@ -105,7 +100,6 @@ public class Subscription {
|
|||
for (iterator = this.listeners.iterator(); iterator.hasNext();) {
|
||||
listener = iterator.next();
|
||||
|
||||
// this.c++;
|
||||
invocation.invoke(listener, handler, handleIndex, message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package dorkbox.util.messagebus.subscription;
|
||||
|
||||
import dorkbox.util.messagebus.common.*;
|
||||
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +39,7 @@ public class SubscriptionManager {
|
|||
private final Map<Class<?>, Boolean> nonListeners;
|
||||
|
||||
// shortcut publication if we know there is no possibility of varArg (ie: a method that has an array as arguments)
|
||||
private final VarArgPossibility varArgPossibility = new VarArgPossibility();
|
||||
private AtomicBoolean varArgPossibility = new AtomicBoolean(false);
|
||||
|
||||
// all subscriptions per message type. We perpetually KEEP the types, as this lowers the amount of locking required
|
||||
// this is the primary list for dispatching a specific message
|
||||
|
@ -52,9 +54,11 @@ public class SubscriptionManager {
|
|||
private final ConcurrentMap<Class<?>, Subscription[]> subscriptionsPerListener;
|
||||
|
||||
|
||||
private final SuperClassUtils superClass;
|
||||
private final VarArgUtils varArgUtils;
|
||||
|
||||
|
||||
|
||||
private final StampedLock lock = new StampedLock();
|
||||
// private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
|
@ -72,12 +76,13 @@ public class SubscriptionManager {
|
|||
this.subscriptionsPerListener = new ConcurrentHashMapV8<Class<?>, Subscription[]>();
|
||||
}
|
||||
|
||||
this.utils = new SubscriptionUtils(this.subscriptionsPerMessageSingle, this.subscriptionsPerMessageMulti, loadFactor,
|
||||
this.superClass = new SuperClassUtils(loadFactor, 1);
|
||||
this.utils = new SubscriptionUtils(superClass, this.subscriptionsPerMessageSingle, this.subscriptionsPerMessageMulti, loadFactor,
|
||||
numberOfThreads);
|
||||
|
||||
// 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
|
||||
this.varArgUtils = new VarArgUtils(this.utils, this.subscriptionsPerMessageSingle, loadFactor, numberOfThreads);
|
||||
this.varArgUtils = new VarArgUtils(this.utils, superClass, this.subscriptionsPerMessageSingle, loadFactor, numberOfThreads);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
@ -91,10 +96,6 @@ public class SubscriptionManager {
|
|||
clearConcurrentCollections();
|
||||
}
|
||||
|
||||
public boolean hasVarArgPossibility() {
|
||||
return this.varArgPossibility.get();
|
||||
}
|
||||
|
||||
public void subscribe(Object listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
|
@ -138,8 +139,6 @@ public class SubscriptionManager {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
VarArgPossibility varArgPossibility = this.varArgPossibility;
|
||||
Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
|
||||
HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti = this.subscriptionsPerMessageMulti;
|
||||
|
||||
|
@ -160,7 +159,7 @@ public class SubscriptionManager {
|
|||
|
||||
// now add this subscription to each of the handled types
|
||||
subsForPublication = getSubsForPublication(messageHandler.getHandledMessages(), subsPerMessageSingle,
|
||||
subsPerMessageMulti, varArgPossibility);
|
||||
subsPerMessageMulti);
|
||||
subsForPublication.add(subscription); // activates this sub for publication
|
||||
}
|
||||
|
||||
|
@ -177,7 +176,7 @@ public class SubscriptionManager {
|
|||
}
|
||||
|
||||
// subscriptions already exist and must only be updated
|
||||
// only getSubscriptions 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;
|
||||
|
||||
for (int i = 0; i < subscriptions.length; i++) {
|
||||
|
@ -190,8 +189,7 @@ public class SubscriptionManager {
|
|||
// also puts it into the correct map if it's not already there
|
||||
private Collection<Subscription> getSubsForPublication(final Class<?>[] messageHandlerTypes,
|
||||
final Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle,
|
||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti,
|
||||
final VarArgPossibility varArgPossibility) {
|
||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti) {
|
||||
|
||||
final int size = messageHandlerTypes.length;
|
||||
|
||||
|
@ -208,11 +206,11 @@ public class SubscriptionManager {
|
|||
|
||||
boolean isArray = type0.isArray();
|
||||
if (isArray) {
|
||||
varArgPossibility.set(true);
|
||||
varArgPossibility.lazySet(true);
|
||||
}
|
||||
|
||||
// cache the super classes
|
||||
// utils.cacheSuperClasses(type0, isArray);
|
||||
//utils.cacheSuperClasses(type0, isArray);
|
||||
|
||||
subsPerMessageSingle.put(type0, subs);
|
||||
}
|
||||
|
@ -222,7 +220,7 @@ public class SubscriptionManager {
|
|||
case 2: {
|
||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||
// subsPerType = subHolderSingle.getSubscriptions();
|
||||
// subsPerType = subHolderSingle.publish();
|
||||
//
|
||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, type0, types[1]);
|
||||
// if (putIfAbsent != null) {
|
||||
|
@ -240,7 +238,7 @@ public class SubscriptionManager {
|
|||
case 3: {
|
||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||
// subsPerType = subHolderSingle.getSubscriptions();
|
||||
// subsPerType = subHolderSingle.publish();
|
||||
//
|
||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, type0, types[1], types[2]);
|
||||
// if (putIfAbsent != null) {
|
||||
|
@ -259,7 +257,7 @@ public class SubscriptionManager {
|
|||
default: {
|
||||
// the HashMapTree uses read/write locks, so it is only accessible one thread at a time
|
||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||
// subsPerType = subHolderSingle.getSubscriptions();
|
||||
// subsPerType = subHolderSingle.publish();
|
||||
//
|
||||
// Collection<Subscription> putIfAbsent = subsPerMessageMulti.putIfAbsent(subsPerType, types);
|
||||
// if (putIfAbsent != null) {
|
||||
|
@ -356,27 +354,40 @@ public class SubscriptionManager {
|
|||
}
|
||||
|
||||
// never return null
|
||||
public final Subscription[] getSubscriptions(final Class<?> messageClass) {
|
||||
ArrayList<Subscription> collection;
|
||||
|
||||
|
||||
public final Subscription[] getSubscriptions(final Class<?> messageClass, boolean isArray) {
|
||||
StampedLock lock = this.lock;
|
||||
long stamp = lock.readLock();
|
||||
// Lock readLock = this.lock.readLock();
|
||||
// readLock.lock();
|
||||
|
||||
final Subscription[] subscriptions = getSubscriptions_NL(messageClass, isArray);
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
// readLock.unlock();
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
// never return null
|
||||
private Subscription[] getSubscriptions_NL(final Class<?> messageClass, boolean isArray) {
|
||||
ArrayList<Subscription> collection;
|
||||
|
||||
collection = this.subscriptionsPerMessageSingle.get(messageClass); // can return null
|
||||
|
||||
// now getSubscriptions superClasses
|
||||
ArrayList<Subscription> superSubscriptions = this.utils.getSuperSubscriptions(messageClass); // NOT return null
|
||||
// now publish superClasses
|
||||
ArrayList<Subscription> superSubscriptions = this.utils.getSuperSubscriptions(messageClass, isArray); // NOT return null
|
||||
|
||||
if (collection != null) {
|
||||
collection = new ArrayList<Subscription>(collection);
|
||||
if (!superSubscriptions.isEmpty()) {
|
||||
collection.addAll(superSubscriptions);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!superSubscriptions.isEmpty()) {
|
||||
collection = superSubscriptions;
|
||||
}
|
||||
}
|
||||
|
||||
final Subscription[] subscriptions;
|
||||
if (collection != null) {
|
||||
|
@ -386,9 +397,6 @@ public class SubscriptionManager {
|
|||
subscriptions = null;
|
||||
}
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
// readLock.unlock();
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
|
@ -403,7 +411,7 @@ public class SubscriptionManager {
|
|||
// readLock.lock();
|
||||
//
|
||||
// try {
|
||||
// collection = this.subscriptionsPerMessageSingle.getSubscriptions(messageType);
|
||||
// collection = this.subscriptionsPerMessageSingle.publish(messageType);
|
||||
// if (collection != null) {
|
||||
// subscriptions = collection.toArray(EMPTY);
|
||||
// }
|
||||
|
@ -416,7 +424,7 @@ public class SubscriptionManager {
|
|||
//
|
||||
//// long stamp = this.lock.tryOptimisticRead(); // non blocking
|
||||
////
|
||||
//// collection = this.subscriptionsPerMessageSingle.getSubscriptions(messageType);
|
||||
//// collection = this.subscriptionsPerMessageSingle.publish(messageType);
|
||||
//// if (collection != null) {
|
||||
////// subscriptions = new ArrayDeque<>(collection);
|
||||
//// subscriptions = new ArrayList<>(collection);
|
||||
|
@ -429,7 +437,7 @@ public class SubscriptionManager {
|
|||
//// if (!this.lock.validate(stamp)) { // if a write occurred, try again with a read lock
|
||||
//// stamp = this.lock.readLock();
|
||||
//// try {
|
||||
//// collection = this.subscriptionsPerMessageSingle.getSubscriptions(messageType);
|
||||
//// collection = this.subscriptionsPerMessageSingle.publish(messageType);
|
||||
//// if (collection != null) {
|
||||
////// subscriptions = new ArrayDeque<>(collection);
|
||||
//// subscriptions = new ArrayList<>(collection);
|
||||
|
@ -480,12 +488,6 @@ public class SubscriptionManager {
|
|||
return this.subscriptionsPerMessageMulti.get(messageTypes);
|
||||
}
|
||||
|
||||
// CAN NOT RETURN NULL
|
||||
// check to see if the messageType can convert/publish to the "array" version, without the hit to JNI
|
||||
// and then, returns the array'd version subscriptions
|
||||
public ConcurrentSet<Subscription> getVarArgSubscriptions(Class<?> messageClass) {
|
||||
return this.varArgUtils.getVarArgSubscriptions(messageClass);
|
||||
}
|
||||
|
||||
// CAN NOT RETURN NULL
|
||||
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
||||
|
@ -527,4 +529,103 @@ public class SubscriptionManager {
|
|||
public Collection<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2, Class<?> superType3) {
|
||||
return this.utils.getSuperSubscriptions(superType1, superType2, superType3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if subscriptions were published
|
||||
*/
|
||||
public boolean publishExact(Object message) throws Throwable {
|
||||
final Class<?> messageClass = message.getClass();
|
||||
final boolean isArray = messageClass.isArray();
|
||||
|
||||
StampedLock lock = this.lock;
|
||||
long stamp = lock.readLock();
|
||||
|
||||
final Subscription[] subscriptions = getSubscriptions_NL(messageClass, isArray);
|
||||
Subscription sub;
|
||||
|
||||
// Run subscriptions
|
||||
if (subscriptions != null) {
|
||||
for (int i = 0; i < subscriptions.length; i++) {
|
||||
sub = subscriptions[i];
|
||||
sub.publish(message);
|
||||
}
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
return true;
|
||||
}
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if subscriptions were published
|
||||
*/
|
||||
public boolean publish(Object message) throws Throwable {
|
||||
final Class<?> messageClass = message.getClass();
|
||||
final boolean isArray = messageClass.isArray();
|
||||
|
||||
StampedLock lock = this.lock;
|
||||
long stamp = lock.readLock();
|
||||
|
||||
|
||||
final Subscription[] subscriptions = getSubscriptions_NL(messageClass, isArray);
|
||||
Subscription sub;
|
||||
|
||||
// Run subscriptions
|
||||
if (subscriptions != null) {
|
||||
for (int i = 0; i < subscriptions.length; i++) {
|
||||
sub = subscriptions[i];
|
||||
sub.publish(message);
|
||||
}
|
||||
|
||||
// publish to var arg, only if not already an array
|
||||
if (varArgPossibility.get() && !isArray) {
|
||||
final ArrayList<Subscription> varArgSubscriptions = varArgUtils.getVarArgSubscriptions(messageClass);
|
||||
|
||||
if (varArgSubscriptions != null && !varArgSubscriptions.isEmpty()) {
|
||||
Object[] asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||
asArray[0] = message;
|
||||
|
||||
Iterator<Subscription> iterator;
|
||||
for (iterator = varArgSubscriptions.iterator(); iterator.hasNext(); ) {
|
||||
sub = iterator.next();
|
||||
|
||||
sub.publish(asArray);
|
||||
}
|
||||
|
||||
// now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
final Collection<Subscription> varArgSuperSubscriptions = getVarArgSuperSubscriptions(messageClass);
|
||||
if (varArgSuperSubscriptions != null && !varArgSuperSubscriptions.isEmpty()) {
|
||||
for (iterator = varArgSubscriptions.iterator(); iterator.hasNext(); ) {
|
||||
sub = iterator.next();
|
||||
|
||||
sub.publish(asArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// now publish array based superClasses (but only if those ALSO accept vararg)
|
||||
final Collection<Subscription> varArgSuperSubscriptions = getVarArgSuperSubscriptions(messageClass);
|
||||
if (varArgSuperSubscriptions != null && !varArgSuperSubscriptions.isEmpty()) {
|
||||
Object[] asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||
asArray[0] = message;
|
||||
|
||||
Iterator<Subscription> iterator;
|
||||
for (iterator = varArgSuperSubscriptions.iterator(); iterator.hasNext(); ) {
|
||||
sub = iterator.next();
|
||||
|
||||
sub.publish(asArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
return true;
|
||||
}
|
||||
|
||||
lock.unlockRead(stamp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,10 @@ public class SubscriptionUtils {
|
|||
private final HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti;
|
||||
|
||||
|
||||
public SubscriptionUtils(Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle,
|
||||
public SubscriptionUtils(SuperClassUtils superClass, Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle,
|
||||
HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti, float loadFactor,
|
||||
int stripeSize) {
|
||||
|
||||
this.superClass = new SuperClassUtils(loadFactor, 1);
|
||||
this.superClass = superClass;
|
||||
|
||||
this.subscriptionsPerMessageSingle = subscriptionsPerMessageSingle;
|
||||
this.subscriptionsPerMessageMulti = subscriptionsPerMessageMulti;
|
||||
|
@ -75,7 +74,7 @@ public class SubscriptionUtils {
|
|||
// long stamp = lock.tryOptimisticRead();
|
||||
//
|
||||
// if (stamp > 0) {
|
||||
// ArrayList<Class<?>> arrayList = local.getSubscriptions(clazz);
|
||||
// ArrayList<Class<?>> arrayList = local.publish(clazz);
|
||||
// if (arrayList != null) {
|
||||
// classes = arrayList.toArray(SUPER_CLASS_EMPTY);
|
||||
//
|
||||
|
@ -84,7 +83,7 @@ public class SubscriptionUtils {
|
|||
// } else {
|
||||
// stamp = lock.readLock();
|
||||
//
|
||||
// arrayList = local.getSubscriptions(clazz);
|
||||
// arrayList = local.publish(clazz);
|
||||
// if (arrayList != null) {
|
||||
// classes = arrayList.toArray(SUPER_CLASS_EMPTY);
|
||||
// lock.unlockRead(stamp);
|
||||
|
@ -94,7 +93,7 @@ public class SubscriptionUtils {
|
|||
// }
|
||||
// }
|
||||
//
|
||||
// // unable to getSubscriptions a valid subscription. Have to acquire a write lock
|
||||
// // unable to publish a valid subscription. Have to acquire a write lock
|
||||
// long origStamp = stamp;
|
||||
// if ((stamp = lock.tryConvertToWriteLock(stamp)) == 0) {
|
||||
// lock.unlockRead(origStamp);
|
||||
|
@ -102,7 +101,7 @@ public class SubscriptionUtils {
|
|||
// }
|
||||
//
|
||||
//
|
||||
// // getSubscriptions all super types of class
|
||||
// // publish all super types of class
|
||||
// Collection<Class<?>> superTypes = ReflectionUtils.getSuperTypes(clazz);
|
||||
// ArrayList<Class<?>> arrayList = new ArrayList<Class<?>>(superTypes.size());
|
||||
// Iterator<Class<?>> iterator;
|
||||
|
@ -144,25 +143,25 @@ public class SubscriptionUtils {
|
|||
*
|
||||
* @return CAN NOT RETURN NULL
|
||||
*/
|
||||
public final ArrayList<Subscription> getSuperSubscriptions(final Class<?> clazz) {
|
||||
public final ArrayList<Subscription> getSuperSubscriptions(final Class<?> clazz, boolean isArray) {
|
||||
// whenever our subscriptions change, this map is cleared.
|
||||
final Map<Class<?>, ArrayList<Subscription>> local = this.superClassSubscriptions;
|
||||
|
||||
ArrayList<Subscription> superSubscriptions = local.get(clazz);
|
||||
|
||||
if (superSubscriptions == null) {
|
||||
// types was not empty, so getSubscriptions subscriptions for each type and collate them
|
||||
// types was not empty, so publish subscriptions for each type and collate them
|
||||
final Map<Class<?>, ArrayList<Subscription>> local2 = this.subscriptionsPerMessageSingle;
|
||||
|
||||
// save the subscriptions
|
||||
final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz, clazz.isArray()); // never returns null, cached response
|
||||
final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz, isArray); // never returns null, cached response
|
||||
|
||||
Class<?> superClass;
|
||||
ArrayList<Subscription> superSubs;
|
||||
Subscription sub;
|
||||
|
||||
final int length = superClasses.length;
|
||||
int superSubLengh;
|
||||
int superSubLength;
|
||||
superSubscriptions = new ArrayList<Subscription>(length);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
@ -170,8 +169,8 @@ public class SubscriptionUtils {
|
|||
superSubs = local2.get(superClass);
|
||||
|
||||
if (superSubs != null) {
|
||||
superSubLengh = superSubs.size();
|
||||
for (int j = 0; j < superSubLengh; j++) {
|
||||
superSubLength = superSubs.size();
|
||||
for (int j = 0; j < superSubLength; j++) {
|
||||
sub = superSubs.get(j);
|
||||
|
||||
if (sub.acceptsSubtypes()) {
|
||||
|
@ -181,6 +180,7 @@ public class SubscriptionUtils {
|
|||
}
|
||||
}
|
||||
|
||||
superSubscriptions.trimToSize();
|
||||
local.put(clazz, superSubscriptions);
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ public class SubscriptionUtils {
|
|||
// subsPerType = subsPerTypeLeaf.getValue();
|
||||
// } else {
|
||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||
// subsPerType = subHolderSingle.getSubscriptions();
|
||||
// subsPerType = subHolderSingle.publish();
|
||||
//
|
||||
// // cache our subscriptions for super classes, so that their access can be fast!
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2);
|
||||
|
@ -288,7 +288,7 @@ public class SubscriptionUtils {
|
|||
// subsPerType = subsPerTypeLeaf.getValue();
|
||||
// } else {
|
||||
// SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||
// subsPerType = subHolderSingle.getSubscriptions();
|
||||
// subsPerType = subHolderSingle.publish();
|
||||
//
|
||||
// // cache our subscriptions for super classes, so that their access can be fast!
|
||||
// ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2, superType3);
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SynchronizedHandlerTest extends MessageBusTest {
|
|||
@Synchronized
|
||||
public void handleMessage(Object o){
|
||||
counter.getAndIncrement();
|
||||
// System.err.println(counter.getSubscriptions());
|
||||
// System.err.println(counter.publish());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class SubscriptionValidator extends AssertSupport {
|
|||
|
||||
// we split subs + superSubs into TWO calls.
|
||||
Collection<Subscription> collection = new ArrayDeque<Subscription>(8);
|
||||
Subscription[] subscriptions = manager.getSubscriptions(messageType);
|
||||
Subscription[] subscriptions = manager.getSubscriptions(messageType, messageType.isArray());
|
||||
if (subscriptions != null) {
|
||||
collection.addAll(Arrays.asList(subscriptions));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user