stamped lock, switched to ConcurrentSet+CLQ
This commit is contained in:
parent
a4924e58be
commit
9a787506ee
@ -1,19 +1,17 @@
|
|||||||
package dorkbox.util.messagebus;
|
package dorkbox.util.messagebus;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.jctools.util.Pow2;
|
import org.jctools.util.Pow2;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.DeadMessage;
|
import dorkbox.util.messagebus.common.DeadMessage;
|
||||||
import dorkbox.util.messagebus.common.NamedThreadFactory;
|
import dorkbox.util.messagebus.common.NamedThreadFactory;
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSet;
|
|
||||||
import dorkbox.util.messagebus.common.simpleq.MpmcMultiTransferArrayQueue;
|
import dorkbox.util.messagebus.common.simpleq.MpmcMultiTransferArrayQueue;
|
||||||
import dorkbox.util.messagebus.common.simpleq.MultiNode;
|
import dorkbox.util.messagebus.common.simpleq.MultiNode;
|
||||||
import dorkbox.util.messagebus.common.thread.BooleanHolder;
|
import dorkbox.util.messagebus.common.thread.BooleanHolder;
|
||||||
import dorkbox.util.messagebus.common.thread.BooleanThreadHolder;
|
import dorkbox.util.messagebus.common.thread.BooleanThreadHolder;
|
||||||
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
|
||||||
import dorkbox.util.messagebus.error.IPublicationErrorHandler;
|
import dorkbox.util.messagebus.error.IPublicationErrorHandler;
|
||||||
import dorkbox.util.messagebus.error.PublicationError;
|
import dorkbox.util.messagebus.error.PublicationError;
|
||||||
import dorkbox.util.messagebus.subscription.Subscription;
|
import dorkbox.util.messagebus.subscription.Subscription;
|
||||||
@ -157,21 +155,6 @@ public class MultiMBassador implements IMessageBus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void subscribe(final Object listener) {
|
|
||||||
MultiMBassador.this.subscriptionManager.subscribe(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unsubscribe(final Object listener) {
|
|
||||||
MultiMBassador.this.subscriptionManager.unsubscribe(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean hasPendingMessages() {
|
|
||||||
return this.dispatchQueue.hasPendingMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
for (Thread t : this.threads) {
|
for (Thread t : this.threads) {
|
||||||
@ -194,6 +177,21 @@ public class MultiMBassador implements IMessageBus {
|
|||||||
this.subscriptionManager.shutdown();
|
this.subscriptionManager.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void subscribe(final Object listener) {
|
||||||
|
MultiMBassador.this.subscriptionManager.subscribe(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsubscribe(final Object listener) {
|
||||||
|
MultiMBassador.this.subscriptionManager.unsubscribe(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean hasPendingMessages() {
|
||||||
|
return this.dispatchQueue.hasPendingMessages();
|
||||||
|
}
|
||||||
|
|
||||||
private final BooleanThreadHolder booleanThreadLocal = new BooleanThreadHolder();
|
private final BooleanThreadHolder booleanThreadLocal = new BooleanThreadHolder();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -201,70 +199,80 @@ public class MultiMBassador implements IMessageBus {
|
|||||||
SubscriptionManager manager = this.subscriptionManager;
|
SubscriptionManager manager = this.subscriptionManager;
|
||||||
|
|
||||||
Class<?> messageClass = message.getClass();
|
Class<?> messageClass = message.getClass();
|
||||||
ConcurrentSet<Subscription> subscriptions = manager.getSubscriptionsByMessageType(messageClass);
|
Collection<Subscription> subscriptions = manager.getSubscriptionsByMessageType(messageClass); // can return null
|
||||||
|
|
||||||
BooleanHolder subsPublished = this.booleanThreadLocal.get();
|
BooleanHolder subsPublished = this.booleanThreadLocal.get();
|
||||||
subsPublished.bool = false;
|
subsPublished.bool = false;
|
||||||
|
|
||||||
|
Iterator<Subscription> iterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
|
||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
for (Subscription sub : subscriptions) {
|
for (iterator = subscriptions.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
|
|
||||||
// this catches all exception types
|
// this catches all exception types
|
||||||
sub.publishToSubscription(this, subsPublished, message);
|
sub.publishToSubscription(this, subsPublished, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.forceExactMatches) {
|
if (!this.forceExactMatches) {
|
||||||
ConcurrentSet<Subscription> superSubscriptions = manager.getSuperSubscriptions(messageClass);
|
Collection<Subscription> superSubscriptions = manager.getSuperSubscriptions(messageClass);
|
||||||
// now get superClasses
|
// now get superClasses
|
||||||
if (superSubscriptions != null) {
|
if (superSubscriptions != null) {
|
||||||
for (Subscription sub : superSubscriptions) {
|
for (iterator = superSubscriptions.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
|
|
||||||
// this catches all exception types
|
// this catches all exception types
|
||||||
sub.publishToSubscription(this, subsPublished, message);
|
sub.publishToSubscription(this, subsPublished, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// publish to var arg, only if not already an array
|
// // publish to var arg, only if not already an array
|
||||||
if (manager.hasVarArgPossibility() && !manager.utils.isArray(messageClass)) {
|
// if (manager.hasVarArgPossibility() && !manager.utils.isArray(messageClass)) {
|
||||||
Object[] asArray = null;
|
// Object[] asArray = null;
|
||||||
|
//
|
||||||
ConcurrentSet<Subscription> varargSubscriptions = manager.getVarArgSubscriptions(messageClass);
|
// ConcurrentSet<Subscription> varargSubscriptions = manager.getVarArgSubscriptions(messageClass);
|
||||||
if (varargSubscriptions != null && !varargSubscriptions.isEmpty()) {
|
// if (varargSubscriptions != null && !varargSubscriptions.isEmpty()) {
|
||||||
asArray = (Object[]) Array.newInstance(messageClass, 1);
|
// asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||||
asArray[0] = message;
|
// asArray[0] = message;
|
||||||
|
//
|
||||||
for (Subscription sub : varargSubscriptions) {
|
// for (iterator = varargSubscriptions.iterator(); iterator.hasNext();) {
|
||||||
// this catches all exception types
|
// sub = iterator.next();
|
||||||
sub.publishToSubscription(this, subsPublished, asArray);
|
// // this catches all exception types
|
||||||
}
|
// sub.publishToSubscription(this, subsPublished, asArray);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
ConcurrentSet<Subscription> varargSuperSubscriptions = manager.getVarArgSuperSubscriptions(messageClass);
|
//
|
||||||
// now get array based superClasses (but only if those ALSO accept vararg)
|
// ConcurrentSet<Subscription> varargSuperSubscriptions = manager.getVarArgSuperSubscriptions(messageClass);
|
||||||
if (varargSuperSubscriptions != null && !varargSuperSubscriptions.isEmpty()) {
|
// // now get array based superClasses (but only if those ALSO accept vararg)
|
||||||
if (asArray == null) {
|
// if (varargSuperSubscriptions != null && !varargSuperSubscriptions.isEmpty()) {
|
||||||
asArray = (Object[]) Array.newInstance(messageClass, 1);
|
// if (asArray == null) {
|
||||||
asArray[0] = message;
|
// asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||||
}
|
// asArray[0] = message;
|
||||||
for (Subscription sub : varargSuperSubscriptions) {
|
// }
|
||||||
// this catches all exception types
|
// for (iterator = varargSuperSubscriptions.iterator(); iterator.hasNext();) {
|
||||||
sub.publishToSubscription(this, subsPublished, asArray);
|
// sub = iterator.next();
|
||||||
}
|
// // this catches all exception types
|
||||||
}
|
// sub.publishToSubscription(this, subsPublished, asArray);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subsPublished.bool) {
|
if (!subsPublished.bool) {
|
||||||
// Dead Event must EXACTLY MATCH (no subclasses)
|
// Dead Event must EXACTLY MATCH (no subclasses)
|
||||||
ConcurrentSet<Subscription> deadSubscriptions = manager.getSubscriptionsByMessageType(DeadMessage.class);
|
Collection<Subscription> deadSubscriptions = manager.getSubscriptionsByMessageType(DeadMessage.class);
|
||||||
if (deadSubscriptions != null && !deadSubscriptions.isEmpty()) {
|
if (deadSubscriptions != null && !deadSubscriptions.isEmpty()) {
|
||||||
DeadMessage deadMessage = new DeadMessage(message);
|
DeadMessage deadMessage = new DeadMessage(message);
|
||||||
|
|
||||||
for (Subscription sub2 : deadSubscriptions) {
|
for (iterator = deadSubscriptions.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
// this catches all exception types
|
// this catches all exception types
|
||||||
sub2.publishToSubscription(this, subsPublished, deadMessage);
|
sub.publishToSubscription(this, subsPublished, deadMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package dorkbox.util.messagebus;
|
package dorkbox.util.messagebus;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.ConcurrentHashMapV8;
|
import dorkbox.util.messagebus.common.ConcurrentHashMapV8;
|
||||||
import dorkbox.util.messagebus.common.HashMapTree;
|
import dorkbox.util.messagebus.common.HashMapTree;
|
||||||
import dorkbox.util.messagebus.common.ISetEntry;
|
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSet;
|
|
||||||
import dorkbox.util.messagebus.common.SubscriptionUtils;
|
import dorkbox.util.messagebus.common.SubscriptionUtils;
|
||||||
import dorkbox.util.messagebus.common.VarArgPossibility;
|
import dorkbox.util.messagebus.common.VarArgPossibility;
|
||||||
import dorkbox.util.messagebus.common.VarArgUtils;
|
import dorkbox.util.messagebus.common.VarArgUtils;
|
||||||
@ -51,8 +50,8 @@ public class SubscriptionManager {
|
|||||||
// all subscriptions per message type. We perpetually KEEP the types, as this lowers the amount of locking required
|
// 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
|
// this is the primary list for dispatching a specific message
|
||||||
// write access is synchronized and happens only when a listener of a specific class is registered the first time
|
// write access is synchronized and happens only when a listener of a specific class is registered the first time
|
||||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerMessageSingle;
|
private final ConcurrentMap<Class<?>, Collection<Subscription>> subscriptionsPerMessageSingle;
|
||||||
private final HashMapTree<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerMessageMulti;
|
private final HashMapTree<Class<?>, Collection<Subscription>> subscriptionsPerMessageMulti;
|
||||||
|
|
||||||
// all subscriptions per messageHandler type
|
// all subscriptions per messageHandler type
|
||||||
// this map provides fast access for subscribing and unsubscribing
|
// this map provides fast access for subscribing and unsubscribing
|
||||||
@ -61,12 +60,6 @@ public class SubscriptionManager {
|
|||||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerListener;
|
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerListener;
|
||||||
|
|
||||||
|
|
||||||
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
|
|
||||||
// it's a hit on SUB/UNSUB, but REALLY improves performance on handlers
|
|
||||||
// it's faster to create a new one for SUB/UNSUB than it is to clear() on the original one
|
|
||||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> superClassSubscriptions;
|
|
||||||
private final HashMapTree<Class<?>, ConcurrentSet<Subscription>> superClassSubscriptionsMulti;
|
|
||||||
|
|
||||||
private final VarArgUtils varArgUtils;
|
private final VarArgUtils varArgUtils;
|
||||||
|
|
||||||
// stripe size of maps for concurrency
|
// stripe size of maps for concurrency
|
||||||
@ -75,36 +68,31 @@ public class SubscriptionManager {
|
|||||||
private final SubscriptionHolder subHolderSingle;
|
private final SubscriptionHolder subHolderSingle;
|
||||||
private final SubscriptionHolder subHolderConcurrent;
|
private final SubscriptionHolder subHolderConcurrent;
|
||||||
|
|
||||||
|
|
||||||
SubscriptionManager(int numberOfThreads) {
|
SubscriptionManager(int numberOfThreads) {
|
||||||
this.STRIPE_SIZE = numberOfThreads;
|
this.STRIPE_SIZE = numberOfThreads;
|
||||||
|
|
||||||
this.utils = new SubscriptionUtils(LOAD_FACTOR, numberOfThreads);
|
float loadFactor = SubscriptionManager.LOAD_FACTOR;
|
||||||
|
|
||||||
// modified ONLY during SUB/UNSUB
|
// modified ONLY during SUB/UNSUB
|
||||||
{
|
{
|
||||||
this.nonListeners = new ConcurrentHashMapV8<Class<?>, Boolean>(4, SubscriptionManager.LOAD_FACTOR);
|
this.nonListeners = new ConcurrentHashMapV8<Class<?>, Boolean>(4, loadFactor, this.STRIPE_SIZE);
|
||||||
|
|
||||||
this.subscriptionsPerMessageSingle = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(32, SubscriptionManager.LOAD_FACTOR, 1);
|
this.subscriptionsPerMessageSingle = new ConcurrentHashMapV8<Class<?>, Collection<Subscription>>(4, loadFactor, this.STRIPE_SIZE);
|
||||||
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ConcurrentSet<Subscription>>(4, SubscriptionManager.LOAD_FACTOR);
|
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, Collection<Subscription>>(4, loadFactor);
|
||||||
|
|
||||||
// only used during SUB/UNSUB
|
// only used during SUB/UNSUB
|
||||||
this.subscriptionsPerListener = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(32, SubscriptionManager.LOAD_FACTOR, 1);
|
this.subscriptionsPerListener = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(4, loadFactor, this.STRIPE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// modified by N threads
|
this.utils = new SubscriptionUtils(this.subscriptionsPerMessageSingle, this.subscriptionsPerMessageMulti, loadFactor, numberOfThreads);
|
||||||
{
|
|
||||||
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
|
|
||||||
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
|
||||||
this.superClassSubscriptions = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(32, SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
|
||||||
this.superClassSubscriptionsMulti = new HashMapTree<Class<?>, ConcurrentSet<Subscription>>(4, SubscriptionManager.LOAD_FACTOR);
|
|
||||||
|
|
||||||
// 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, this.subscriptionsPerMessageSingle, SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
this.varArgUtils = new VarArgUtils(this.utils, this.subscriptionsPerMessageSingle, loadFactor, this.STRIPE_SIZE);
|
||||||
}
|
|
||||||
|
|
||||||
this.subHolderSingle = new SubscriptionHolder(SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
this.subHolderSingle = new SubscriptionHolder(loadFactor, this.STRIPE_SIZE);
|
||||||
this.subHolderConcurrent = new SubscriptionHolder(SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
this.subHolderConcurrent = new SubscriptionHolder(loadFactor, this.STRIPE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
@ -146,7 +134,7 @@ public class SubscriptionManager {
|
|||||||
ConcurrentSet<Subscription> subsPerListener = subsPerListener2.get(listenerClass);
|
ConcurrentSet<Subscription> subsPerListener = subsPerListener2.get(listenerClass);
|
||||||
if (subsPerListener == null) {
|
if (subsPerListener == null) {
|
||||||
// a listener is subscribed for the first time
|
// a listener is subscribed for the first time
|
||||||
StrongConcurrentSet<MessageHandler> messageHandlers = SubscriptionManager.metadataReader.getMessageListener(listenerClass).getHandlers();
|
Collection<MessageHandler> messageHandlers = SubscriptionManager.metadataReader.getMessageListener(listenerClass, LOAD_FACTOR, this.STRIPE_SIZE).getHandlers();
|
||||||
int handlersSize = messageHandlers.size();
|
int handlersSize = messageHandlers.size();
|
||||||
|
|
||||||
if (handlersSize == 0) {
|
if (handlersSize == 0) {
|
||||||
@ -157,15 +145,15 @@ public class SubscriptionManager {
|
|||||||
VarArgPossibility varArgPossibility = this.varArgPossibility;
|
VarArgPossibility varArgPossibility = this.varArgPossibility;
|
||||||
|
|
||||||
subsPerListener = new ConcurrentSet<Subscription>(16, SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
subsPerListener = new ConcurrentSet<Subscription>(16, SubscriptionManager.LOAD_FACTOR, this.STRIPE_SIZE);
|
||||||
ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
|
ConcurrentMap<Class<?>, Collection<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
|
||||||
|
|
||||||
ISetEntry<MessageHandler> current = messageHandlers.head;
|
Iterator<MessageHandler> iterator;
|
||||||
MessageHandler messageHandler;
|
MessageHandler messageHandler;
|
||||||
while (current != null) {
|
Collection<Subscription> subsPerType = null;
|
||||||
messageHandler = current.getValue();
|
|
||||||
current = current.next();
|
for (iterator = messageHandlers.iterator(); iterator.hasNext();) {
|
||||||
|
messageHandler = iterator.next();
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subsPerType = null;
|
|
||||||
|
|
||||||
// now add this subscription to each of the handled types
|
// now add this subscription to each of the handled types
|
||||||
Class<?>[] types = messageHandler.getHandledMessages();
|
Class<?>[] types = messageHandler.getHandledMessages();
|
||||||
@ -176,12 +164,13 @@ public class SubscriptionManager {
|
|||||||
SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||||
subsPerType = subHolderConcurrent.get();
|
subsPerType = subHolderConcurrent.get();
|
||||||
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = subsPerMessageSingle.putIfAbsent(types[0], subsPerType);
|
Collection<Subscription> putIfAbsent = subsPerMessageSingle.putIfAbsent(types[0], subsPerType);
|
||||||
if (putIfAbsent != null) {
|
if (putIfAbsent != null) {
|
||||||
subsPerType = putIfAbsent;
|
subsPerType = putIfAbsent;
|
||||||
} else {
|
} else {
|
||||||
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
||||||
boolean isArray = this.utils.isArray(types[0]);
|
boolean isArray = this.utils.isArray(types[0]);
|
||||||
|
// cache the super classes
|
||||||
this.utils.getSuperClasses(types[0], isArray);
|
this.utils.getSuperClasses(types[0], isArray);
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
varArgPossibility.set(true);
|
varArgPossibility.set(true);
|
||||||
@ -194,11 +183,12 @@ public class SubscriptionManager {
|
|||||||
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||||
subsPerType = subHolderSingle.get();
|
subsPerType = subHolderSingle.get();
|
||||||
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types[0], types[1]);
|
Collection<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types[0], types[1]);
|
||||||
if (putIfAbsent != null) {
|
if (putIfAbsent != null) {
|
||||||
subsPerType = putIfAbsent;
|
subsPerType = putIfAbsent;
|
||||||
} else {
|
} else {
|
||||||
subHolderSingle.set(subHolderSingle.initialValue());
|
subHolderSingle.set(subHolderSingle.initialValue());
|
||||||
|
// cache the super classes
|
||||||
this.utils.getSuperClasses(types[0]);
|
this.utils.getSuperClasses(types[0]);
|
||||||
this.utils.getSuperClasses(types[1]);
|
this.utils.getSuperClasses(types[1]);
|
||||||
}
|
}
|
||||||
@ -209,11 +199,12 @@ public class SubscriptionManager {
|
|||||||
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||||
subsPerType = subHolderSingle.get();
|
subsPerType = subHolderSingle.get();
|
||||||
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types[0], types[1], types[2]);
|
Collection<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types[0], types[1], types[2]);
|
||||||
if (putIfAbsent != null) {
|
if (putIfAbsent != null) {
|
||||||
subsPerType = putIfAbsent;
|
subsPerType = putIfAbsent;
|
||||||
} else {
|
} else {
|
||||||
subHolderSingle.set(subHolderSingle.initialValue());
|
subHolderSingle.set(subHolderSingle.initialValue());
|
||||||
|
// cache the super classes
|
||||||
this.utils.getSuperClasses(types[0]);
|
this.utils.getSuperClasses(types[0]);
|
||||||
this.utils.getSuperClasses(types[1]);
|
this.utils.getSuperClasses(types[1]);
|
||||||
this.utils.getSuperClasses(types[2]);
|
this.utils.getSuperClasses(types[2]);
|
||||||
@ -225,7 +216,7 @@ public class SubscriptionManager {
|
|||||||
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||||
subsPerType = subHolderSingle.get();
|
subsPerType = subHolderSingle.get();
|
||||||
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types);
|
Collection<Subscription> putIfAbsent = this.subscriptionsPerMessageMulti.putIfAbsent(subsPerType, types);
|
||||||
if (putIfAbsent != null) {
|
if (putIfAbsent != null) {
|
||||||
subsPerType = putIfAbsent;
|
subsPerType = putIfAbsent;
|
||||||
} else {
|
} else {
|
||||||
@ -253,8 +244,11 @@ public class SubscriptionManager {
|
|||||||
subsPerListener2.put(listenerClass, subsPerListener);
|
subsPerListener2.put(listenerClass, subsPerListener);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// subscriptions already exist and must only be updated
|
Iterator<Subscription> iterator;
|
||||||
for (Subscription sub : subsPerListener) {
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator = subsPerListener.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
sub.subscribe(listener);
|
sub.subscribe(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,9 +270,13 @@ public class SubscriptionManager {
|
|||||||
clearConcurrentCollections();
|
clearConcurrentCollections();
|
||||||
|
|
||||||
synchronized(listenerClass) {
|
synchronized(listenerClass) {
|
||||||
ConcurrentSet<Subscription> subscriptions = this.subscriptionsPerListener.get(listenerClass);
|
Collection<Subscription> subscriptions = this.subscriptionsPerListener.get(listenerClass);
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
for (Subscription sub : subscriptions) {
|
Iterator<Subscription> iterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator = subscriptions.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
sub.unsubscribe(listener);
|
sub.unsubscribe(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,23 +284,23 @@ public class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearConcurrentCollections() {
|
private void clearConcurrentCollections() {
|
||||||
this.superClassSubscriptions.clear();
|
this.utils.clear();
|
||||||
this.varArgUtils.clear();
|
this.varArgUtils.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN RETURN NULL
|
// CAN RETURN NULL
|
||||||
public final ConcurrentSet<Subscription> getSubscriptionsByMessageType(Class<?> messageType) {
|
public final Collection<Subscription> getSubscriptionsByMessageType(Class<?> messageType) {
|
||||||
return this.subscriptionsPerMessageSingle.get(messageType);
|
return this.subscriptionsPerMessageSingle.get(messageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN RETURN NULL
|
// CAN RETURN NULL
|
||||||
public final ConcurrentSet<Subscription> getSubscriptionsByMessageType(Class<?> messageType1, Class<?> messageType2) {
|
public final Collection<Subscription> getSubscriptionsByMessageType(Class<?> messageType1, Class<?> messageType2) {
|
||||||
return this.subscriptionsPerMessageMulti.get(messageType1, messageType2);
|
return this.subscriptionsPerMessageMulti.get(messageType1, messageType2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CAN RETURN NULL
|
// CAN RETURN NULL
|
||||||
public final ConcurrentSet<Subscription> getSubscriptionsByMessageType(Class<?> messageType1, Class<?> messageType2, Class<?> messageType3) {
|
public final Collection<Subscription> getSubscriptionsByMessageType(Class<?> messageType1, Class<?> messageType2, Class<?> messageType3) {
|
||||||
return this.subscriptionsPerMessageMulti.getValue(messageType1, messageType2, messageType3);
|
return this.subscriptionsPerMessageMulti.getValue(messageType1, messageType2, messageType3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,263 +319,40 @@ public class SubscriptionManager {
|
|||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
||||||
// and then, returns the array'd version subscriptions
|
// and then, returns the array'd version subscriptions
|
||||||
public ConcurrentSet<Subscription> getVarArgSuperSubscriptions(Class<?> messageClass) {
|
public Collection<Subscription> getVarArgSuperSubscriptions(Class<?> messageClass) {
|
||||||
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass);
|
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
||||||
// and then, returns the array'd version subscriptions
|
// and then, returns the array'd version subscriptions
|
||||||
public ConcurrentSet<Subscription> getVarArgSuperSubscriptions(Class<?> messageClass1, Class<?> messageClass2) {
|
public Collection<Subscription> getVarArgSuperSubscriptions(Class<?> messageClass1, Class<?> messageClass2) {
|
||||||
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass1, messageClass2);
|
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass1, messageClass2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
// check to see if the messageType can convert/publish to the "array" superclass version, without the hit to JNI
|
||||||
// and then, returns the array'd version subscriptions
|
// and then, returns the array'd version subscriptions
|
||||||
public ConcurrentSet<Subscription> getVarArgSuperSubscriptions(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
public Collection<Subscription> getVarArgSuperSubscriptions(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass1, messageClass2, messageClass3);
|
return this.varArgUtils.getVarArgSuperSubscriptions(messageClass1, messageClass2, messageClass3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// ALSO checks to see if the superClass accepts subtypes.
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
public final ConcurrentSet<Subscription> getSuperSubscriptions(Class<?> superType) {
|
public final Collection<Subscription> getSuperSubscriptions(Class<?> superType) {
|
||||||
// whenever our subscriptions change, this map is cleared.
|
return this.utils.getSuperSubscriptions(superType);
|
||||||
ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptions;
|
|
||||||
|
|
||||||
SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
|
||||||
ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.get();
|
|
||||||
|
|
||||||
// cache our subscriptions for super classes, so that their access can be fast!
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(superType, subsPerType);
|
|
||||||
if (putIfAbsent == null) {
|
|
||||||
// we are the first one in the map
|
|
||||||
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
|
||||||
|
|
||||||
StrongConcurrentSet<Class<?>> types = this.utils.getSuperClasses(superType);
|
|
||||||
if (types.isEmpty()) {
|
|
||||||
return subsPerType;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<Class<?>, ConcurrentSet<Subscription>> local2 = this.subscriptionsPerMessageSingle;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current1 = null;
|
|
||||||
Class<?> superClass;
|
|
||||||
|
|
||||||
current1 = types.head;
|
|
||||||
while (current1 != null) {
|
|
||||||
superClass = current1.getValue();
|
|
||||||
current1 = current1.next();
|
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subs = local2.get(superClass);
|
|
||||||
if (subs != null) {
|
|
||||||
for (Subscription sub : subs) {
|
|
||||||
if (sub.acceptsSubtypes()) {
|
|
||||||
subsPerType.add(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subsPerType;
|
|
||||||
} else {
|
|
||||||
// someone beat us
|
|
||||||
return putIfAbsent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// ALSO checks to see if the superClass accepts subtypes.
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
public ConcurrentSet<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2) {
|
public Collection<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2) {
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptionsMulti;
|
return this.utils.getSuperSubscriptions(superType1, superType2);
|
||||||
|
|
||||||
// whenever our subscriptions change, this map is cleared.
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> subsPerTypeLeaf = local.getLeaf(superType1, superType2);
|
|
||||||
ConcurrentSet<Subscription> subsPerType = null;
|
|
||||||
|
|
||||||
// we DO NOT care about duplicate, because the answers will be the same
|
|
||||||
if (subsPerTypeLeaf != null) {
|
|
||||||
// if the leaf exists, then the value exists.
|
|
||||||
subsPerType = subsPerTypeLeaf.getValue();
|
|
||||||
} else {
|
|
||||||
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
|
||||||
subsPerType = subHolderSingle.get();
|
|
||||||
|
|
||||||
// cache our subscriptions for super classes, so that their access can be fast!
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2);
|
|
||||||
if (putIfAbsent == null) {
|
|
||||||
// we are the first one in the map
|
|
||||||
subHolderSingle.set(subHolderSingle.initialValue());
|
|
||||||
|
|
||||||
// whenever our subscriptions change, this map is cleared.
|
|
||||||
StrongConcurrentSet<Class<?>> types1 = this.utils.getSuperClasses(superType1);
|
|
||||||
StrongConcurrentSet<Class<?>> types2 = this.utils.getSuperClasses(superType2);
|
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subs;
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> leaf1;
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> leaf2;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current1 = null;
|
|
||||||
Class<?> eventSuperType1;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current2 = null;
|
|
||||||
Class<?> eventSuperType2;
|
|
||||||
|
|
||||||
if (types1 != null) {
|
|
||||||
current1 = types1.head;
|
|
||||||
}
|
|
||||||
while (current1 != null) {
|
|
||||||
eventSuperType1 = current1.getValue();
|
|
||||||
current1 = current1.next();
|
|
||||||
|
|
||||||
boolean type1Matches = eventSuperType1 == superType1;
|
|
||||||
if (type1Matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf1 = this.subscriptionsPerMessageMulti.getLeaf(eventSuperType1);
|
|
||||||
if (leaf1 != null) {
|
|
||||||
if (types2 != null) {
|
|
||||||
current2 = types2.head;
|
|
||||||
}
|
|
||||||
while (current2 != null) {
|
|
||||||
eventSuperType2 = current2.getValue();
|
|
||||||
current2 = current2.next();
|
|
||||||
|
|
||||||
if (type1Matches && eventSuperType2 == superType2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf2 = leaf1.getLeaf(eventSuperType2);
|
|
||||||
|
|
||||||
if (leaf2 != null) {
|
|
||||||
subs = leaf2.getValue();
|
|
||||||
if (subs != null) {
|
|
||||||
for (Subscription sub : subs) {
|
|
||||||
if (sub.acceptsSubtypes()) {
|
|
||||||
subsPerType.add(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// someone beat us
|
|
||||||
subsPerType = putIfAbsent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subsPerType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAN NOT RETURN NULL
|
// CAN NOT RETURN NULL
|
||||||
// ALSO checks to see if the superClass accepts subtypes.
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
public ConcurrentSet<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2, Class<?> superType3) {
|
public Collection<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2, Class<?> superType3) {
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptionsMulti;
|
return this.utils.getSuperSubscriptions(superType1, superType2, superType3);
|
||||||
|
|
||||||
// whenever our subscriptions change, this map is cleared.
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> subsPerTypeLeaf = local.getLeaf(superType1, superType2, superType3);
|
|
||||||
ConcurrentSet<Subscription> subsPerType;
|
|
||||||
|
|
||||||
|
|
||||||
// we DO NOT care about duplicate, because the answers will be the same
|
|
||||||
if (subsPerTypeLeaf != null) {
|
|
||||||
// if the leaf exists, then the value exists.
|
|
||||||
subsPerType = subsPerTypeLeaf.getValue();
|
|
||||||
} else {
|
|
||||||
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
|
||||||
subsPerType = subHolderSingle.get();
|
|
||||||
|
|
||||||
// cache our subscriptions for super classes, so that their access can be fast!
|
|
||||||
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2, superType3);
|
|
||||||
if (putIfAbsent == null) {
|
|
||||||
// we are the first one in the map
|
|
||||||
subHolderSingle.set(subHolderSingle.initialValue());
|
|
||||||
|
|
||||||
StrongConcurrentSet<Class<?>> types1 = this.utils.getSuperClasses(superType1);
|
|
||||||
StrongConcurrentSet<Class<?>> types2 = this.utils.getSuperClasses(superType2);
|
|
||||||
StrongConcurrentSet<Class<?>> types3 = this.utils.getSuperClasses(superType3);
|
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subs;
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> leaf1;
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> leaf2;
|
|
||||||
HashMapTree<Class<?>, ConcurrentSet<Subscription>> leaf3;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current1 = null;
|
|
||||||
Class<?> eventSuperType1;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current2 = null;
|
|
||||||
Class<?> eventSuperType2;
|
|
||||||
|
|
||||||
ISetEntry<Class<?>> current3 = null;
|
|
||||||
Class<?> eventSuperType3;
|
|
||||||
|
|
||||||
if (types1 != null) {
|
|
||||||
current1 = types1.head;
|
|
||||||
}
|
|
||||||
while (current1 != null) {
|
|
||||||
eventSuperType1 = current1.getValue();
|
|
||||||
current1 = current1.next();
|
|
||||||
|
|
||||||
boolean type1Matches = eventSuperType1 == superType1;
|
|
||||||
if (type1Matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf1 = this.subscriptionsPerMessageMulti.getLeaf(eventSuperType1);
|
|
||||||
if (leaf1 != null) {
|
|
||||||
if (types2 != null) {
|
|
||||||
current2 = types2.head;
|
|
||||||
}
|
|
||||||
while (current2 != null) {
|
|
||||||
eventSuperType2 = current2.getValue();
|
|
||||||
current2 = current2.next();
|
|
||||||
|
|
||||||
boolean type12Matches = type1Matches && eventSuperType2 == superType2;
|
|
||||||
if (type12Matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf2 = leaf1.getLeaf(eventSuperType2);
|
|
||||||
|
|
||||||
if (leaf2 != null) {
|
|
||||||
if (types3 != null) {
|
|
||||||
current3 = types3.head;
|
|
||||||
}
|
|
||||||
while (current3 != null) {
|
|
||||||
eventSuperType3 = current3.getValue();
|
|
||||||
current3 = current3.next();
|
|
||||||
|
|
||||||
if (type12Matches && eventSuperType3 == superType3) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf3 = leaf2.getLeaf(eventSuperType3);
|
|
||||||
|
|
||||||
if (leaf3 != null) {
|
|
||||||
subs = leaf3.getValue();
|
|
||||||
if (subs != null) {
|
|
||||||
for (Subscription sub : subs) {
|
|
||||||
if (sub.acceptsSubtypes()) {
|
|
||||||
subsPerType.add(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// someone beat us
|
|
||||||
subsPerType = putIfAbsent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subsPerType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,8 @@ import java.util.Collection;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
|
|
||||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
import dorkbox.util.messagebus.common.thread.StampedLock;
|
||||||
|
|
||||||
|
|
||||||
abstract class pad<T> extends item<T> {
|
abstract class pad<T> extends item<T> {
|
||||||
@ -27,7 +26,7 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
private final transient long ID = id.getAndIncrement();
|
private final transient long ID = id.getAndIncrement();
|
||||||
|
|
||||||
// Internal state
|
// Internal state
|
||||||
protected final transient ReentrantReadWriteUpdateLock lock = new ReentrantReadWriteUpdateLock();
|
protected final StampedLock lock = new StampedLock();
|
||||||
private final transient Map<T, ISetEntry<T>> entries; // maintain a map of entries for O(log n) lookup
|
private final transient Map<T, ISetEntry<T>> entries; // maintain a map of entries for O(log n) lookup
|
||||||
|
|
||||||
volatile long y0, y1, y2, y4, y5, y6 = 7L;
|
volatile long y0, y1, y2, y4, y5, y6 = 7L;
|
||||||
@ -47,25 +46,38 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
}
|
}
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
Lock writeLock = this.lock.writeLock();
|
long stamp = this.lock.readLock();
|
||||||
writeLock.lock();
|
if (this.entries.containsKey(element)) {
|
||||||
|
this.lock.unlockRead(stamp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long newStamp = 0L;
|
||||||
|
while ((newStamp = this.lock.tryConvertToWriteLock(stamp)) == 0) {
|
||||||
|
this.lock.unlockRead(stamp);
|
||||||
|
stamp = this.lock.writeLock();
|
||||||
|
}
|
||||||
|
stamp = newStamp;
|
||||||
|
|
||||||
|
|
||||||
changed = insert(element);
|
changed = insert(element);
|
||||||
writeLock.unlock();
|
this.lock.unlock(stamp);
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object element) {
|
public boolean contains(Object element) {
|
||||||
Lock readLock = this.lock.readLock();
|
long stamp = this.lock.tryOptimisticRead();
|
||||||
ISetEntry<T> entry;
|
|
||||||
try {
|
|
||||||
readLock.lock();
|
|
||||||
entry = this.entries.get(element);
|
|
||||||
|
|
||||||
} finally {
|
ISetEntry<T> entry = this.entries.get(element);
|
||||||
readLock.unlock();
|
|
||||||
|
if (!this.lock.validate(stamp)) {
|
||||||
|
stamp = this.lock.readLock();
|
||||||
|
entry = this.entries.get(element);
|
||||||
|
this.lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry != null && entry.getValue() != null;
|
return entry != null && entry.getValue() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,18 +102,21 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(Collection<? extends T> elements) {
|
public boolean addAll(Collection<? extends T> elements) {
|
||||||
|
StampedLock lock = this.lock;
|
||||||
|
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
Lock writeLock = this.lock.writeLock();
|
long stamp = lock.writeLock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeLock.lock();
|
|
||||||
for (T element : elements) {
|
for (T element : elements) {
|
||||||
if (element != null) {
|
if (element != null) {
|
||||||
changed |= insert(element);
|
changed |= insert(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,16 +125,23 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Object element) {
|
public boolean remove(Object element) {
|
||||||
|
StampedLock lock = this.lock;
|
||||||
|
long stamp = lock.tryOptimisticRead();
|
||||||
|
|
||||||
Lock updateLock = this.lock.updateLock();
|
|
||||||
try {
|
|
||||||
updateLock.lock();
|
|
||||||
ISetEntry<T> entry = this.entries.get(element);
|
ISetEntry<T> entry = this.entries.get(element);
|
||||||
|
|
||||||
if (entry != null && entry.getValue() != null) {
|
if (!lock.validate(stamp)) {
|
||||||
Lock writeLock = this.lock.writeLock();
|
stamp = lock.readLock();
|
||||||
|
entry = this.entries.get(element);
|
||||||
|
lock.unlockRead(stamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == null || entry.getValue() == null) {
|
||||||
|
return false; // fast exit
|
||||||
|
} else {
|
||||||
|
stamp = lock.writeLock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeLock.lock();
|
|
||||||
if (entry != this.head) {
|
if (entry != this.head) {
|
||||||
entry.remove();
|
entry.remove();
|
||||||
} else {
|
} else {
|
||||||
@ -130,13 +152,8 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
this.entries.remove(element);
|
this.entries.remove(element);
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return false; // fast exit
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
updateLock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +163,7 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T[] toArray(T[] a) {
|
public <T2> T2[] toArray(T2[] a) {
|
||||||
return this.entries.entrySet().toArray(a);
|
return this.entries.entrySet().toArray(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,14 +184,12 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
Lock writeLock = this.lock.writeLock();
|
StampedLock lock = this.lock;
|
||||||
try {
|
|
||||||
writeLock.lock();
|
long stamp = lock.writeLock();
|
||||||
this.head = null;
|
this.head = null;
|
||||||
this.entries.clear();
|
this.entries.clear();
|
||||||
} finally {
|
lock.unlockWrite(stamp);
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,6 +211,7 @@ public abstract class AbstractConcurrentSet<T> extends pad<T> implements Set<T>
|
|||||||
if (getClass() != obj.getClass()) {
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
AbstractConcurrentSet other = (AbstractConcurrentSet) obj;
|
AbstractConcurrentSet other = (AbstractConcurrentSet) obj;
|
||||||
if (this.ID != other.ID) {
|
if (this.ID != other.ID) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -4,8 +4,10 @@ import java.lang.annotation.Annotation;
|
|||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.annotations.Handler;
|
import dorkbox.util.messagebus.annotations.Handler;
|
||||||
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
@ -16,13 +18,13 @@ import dorkbox.util.messagebus.annotations.Handler;
|
|||||||
*/
|
*/
|
||||||
public class ReflectionUtils {
|
public class ReflectionUtils {
|
||||||
|
|
||||||
public static StrongConcurrentSetV8<Method> getMethods(Class<?> target) {
|
public static Collection<Method> getMethods(Class<?> target) {
|
||||||
StrongConcurrentSetV8<Method> hashSet = new StrongConcurrentSetV8<Method>(16, .8F);
|
Collection<Method> hashSet = new ConcurrentSet<Method>(16, .8F, 1);
|
||||||
getMethods(target, hashSet);
|
getMethods(target, hashSet);
|
||||||
return hashSet;
|
return hashSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getMethods(Class<?> target, StrongConcurrentSetV8<Method> methods) {
|
private static void getMethods(Class<?> target, Collection<Method> methods) {
|
||||||
try {
|
try {
|
||||||
for (Method method : target.getDeclaredMethods()) {
|
for (Method method : target.getDeclaredMethods()) {
|
||||||
if (getAnnotation(method, Handler.class) != null) {
|
if (getAnnotation(method, Handler.class) != null) {
|
||||||
@ -66,8 +68,8 @@ public class ReflectionUtils {
|
|||||||
* @param from The root class to start with
|
* @param from The root class to start with
|
||||||
* @return A set of classes, each representing a super type of the root class
|
* @return A set of classes, each representing a super type of the root class
|
||||||
*/
|
*/
|
||||||
public static StrongConcurrentSetV8<Class<?>> getSuperTypes(Class<?> from) {
|
public static Collection<Class<?>> getSuperTypes(Class<?> from) {
|
||||||
StrongConcurrentSetV8<Class<?>> superclasses = new StrongConcurrentSetV8<Class<?>>(8, 0.8F);
|
Collection<Class<?>> superclasses = new ConcurrentSet<Class<?>>(8, 0.8F, 1);
|
||||||
|
|
||||||
collectInterfaces( from, superclasses );
|
collectInterfaces( from, superclasses );
|
||||||
|
|
||||||
@ -79,21 +81,19 @@ public class ReflectionUtils {
|
|||||||
return superclasses;
|
return superclasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void collectInterfaces( Class<?> from, StrongConcurrentSetV8<Class<?>> accumulator ) {
|
public static void collectInterfaces( Class<?> from, Collection<Class<?>> accumulator ) {
|
||||||
for ( Class<?> intface : from.getInterfaces() ) {
|
for ( Class<?> intface : from.getInterfaces() ) {
|
||||||
accumulator.add( intface );
|
accumulator.add( intface );
|
||||||
collectInterfaces( intface, accumulator );
|
collectInterfaces( intface, accumulator );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
public static boolean containsOverridingMethod(final Collection<Method> allMethods, final Method methodToCheck) {
|
||||||
public static boolean containsOverridingMethod(final StrongConcurrentSetV8<Method> allMethods, final Method methodToCheck) {
|
Iterator<Method> iterator;
|
||||||
|
|
||||||
ISetEntry<Method> current = allMethods.head;
|
|
||||||
Method method;
|
Method method;
|
||||||
while (current != null) {
|
|
||||||
method = current.getValue();
|
for (iterator = allMethods.iterator(); iterator.hasNext();) {
|
||||||
current = current.next();
|
method = iterator.next();
|
||||||
|
|
||||||
if (isOverriddenBy(methodToCheck, method)) {
|
if (isOverriddenBy(methodToCheck, method)) {
|
||||||
return true;
|
return true;
|
||||||
@ -131,7 +131,7 @@ public class ReflectionUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <A extends Annotation> A getAnnotation( AnnotatedElement from, Class<A> annotationType) {
|
public static <A extends Annotation> A getAnnotation( AnnotatedElement from, Class<A> annotationType) {
|
||||||
A annotation = getAnnotation(from, annotationType, new StrongConcurrentSetV8<AnnotatedElement>(16, .8F));
|
A annotation = getAnnotation(from, annotationType, new ConcurrentSet<AnnotatedElement>(16, .8F, 1));
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,61 @@
|
|||||||
package dorkbox.util.messagebus.common;
|
package dorkbox.util.messagebus.common;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.thread.ClassHolder;
|
import dorkbox.util.messagebus.common.thread.ClassHolder;
|
||||||
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
|
import dorkbox.util.messagebus.common.thread.SubscriptionHolder;
|
||||||
|
import dorkbox.util.messagebus.subscription.Subscription;
|
||||||
|
|
||||||
public class SubscriptionUtils {
|
public class SubscriptionUtils {
|
||||||
|
|
||||||
private final Map<Class<?>, Class<?>> arrayVersionCache;
|
private final Map<Class<?>, Class<?>> arrayVersionCache;
|
||||||
private final Map<Class<?>, Boolean> isArrayCache;
|
private final Map<Class<?>, Boolean> isArrayCache;
|
||||||
|
|
||||||
private final ConcurrentMap<Class<?>, StrongConcurrentSet<Class<?>>> superClassesCache;
|
private final ConcurrentMap<Class<?>, ConcurrentSet<Class<?>>> superClassesCache;
|
||||||
private final ClassHolder classHolderSingle;
|
private final ClassHolder classHolderSingle;
|
||||||
|
|
||||||
|
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
|
||||||
|
// it's a hit on SUB/UNSUB, but REALLY improves performance on handlers
|
||||||
|
// it's faster to create a new one for SUB/UNSUB than it is to clear() on the original one
|
||||||
|
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> superClassSubscriptions;
|
||||||
|
private final HashMapTree<Class<?>, ConcurrentSet<Subscription>> superClassSubscriptionsMulti;
|
||||||
|
|
||||||
public SubscriptionUtils(float loadFactor, int stripeSize) {
|
private final Map<Class<?>, Collection<Subscription>> subscriptionsPerMessageSingle;
|
||||||
this.arrayVersionCache = new ConcurrentHashMapV8<Class<?>, Class<?>>(64, loadFactor, stripeSize);
|
|
||||||
this.isArrayCache = new ConcurrentHashMapV8<Class<?>, Boolean>(64, loadFactor, stripeSize);
|
|
||||||
|
|
||||||
this.superClassesCache = new ConcurrentHashMapV8<Class<?>, StrongConcurrentSet<Class<?>>>(64, loadFactor, stripeSize);
|
|
||||||
this.classHolderSingle = new ClassHolder(loadFactor);
|
private final SubscriptionHolder subHolderSingle;
|
||||||
|
private final SubscriptionHolder subHolderConcurrent;
|
||||||
|
private final HashMapTree<Class<?>, Collection<Subscription>> subscriptionsPerMessageMulti;
|
||||||
|
|
||||||
|
|
||||||
|
public SubscriptionUtils(Map<Class<?>, Collection<Subscription>> subscriptionsPerMessageSingle,
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> subscriptionsPerMessageMulti,
|
||||||
|
float loadFactor, int stripeSize) {
|
||||||
|
|
||||||
|
this.subscriptionsPerMessageSingle = subscriptionsPerMessageSingle;
|
||||||
|
this.subscriptionsPerMessageMulti = subscriptionsPerMessageMulti;
|
||||||
|
this.arrayVersionCache = new ConcurrentHashMapV8<Class<?>, Class<?>>(32, loadFactor, stripeSize);
|
||||||
|
this.isArrayCache = new ConcurrentHashMapV8<Class<?>, Boolean>(32, loadFactor, stripeSize);
|
||||||
|
|
||||||
|
this.superClassesCache = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Class<?>>>(32, loadFactor, stripeSize);
|
||||||
|
this.classHolderSingle = new ClassHolder(loadFactor, stripeSize);
|
||||||
|
|
||||||
|
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
|
||||||
|
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
||||||
|
this.superClassSubscriptions = new ConcurrentHashMapV8<Class<?>, ConcurrentSet<Subscription>>(32, loadFactor, stripeSize);
|
||||||
|
this.superClassSubscriptionsMulti = new HashMapTree<Class<?>, ConcurrentSet<Subscription>>(4, loadFactor);
|
||||||
|
|
||||||
|
this.subHolderSingle = new SubscriptionHolder(loadFactor, stripeSize);
|
||||||
|
this.subHolderConcurrent = new SubscriptionHolder(loadFactor, stripeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.superClassSubscriptions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,31 +63,29 @@ public class SubscriptionUtils {
|
|||||||
* never returns null
|
* never returns null
|
||||||
* never reset, since it never needs to be reset (as the class hierarchy doesn't change at runtime)
|
* never reset, since it never needs to be reset (as the class hierarchy doesn't change at runtime)
|
||||||
*/
|
*/
|
||||||
public StrongConcurrentSet<Class<?>> getSuperClasses(Class<?> clazz) {
|
public Collection<Class<?>> getSuperClasses(Class<?> clazz) {
|
||||||
return getSuperClasses(clazz, isArray(clazz));
|
return getSuperClasses(clazz, isArray(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final StrongConcurrentSet<Class<?>> getSuperClasses(Class<?> clazz, boolean isArray) {
|
public final Collection<Class<?>> getSuperClasses(Class<?> clazz, boolean isArray) {
|
||||||
// this is never reset, since it never needs to be.
|
// this is never reset, since it never needs to be.
|
||||||
ConcurrentMap<Class<?>, StrongConcurrentSet<Class<?>>> local = this.superClassesCache;
|
ConcurrentMap<Class<?>, ConcurrentSet<Class<?>>> local = this.superClassesCache;
|
||||||
|
|
||||||
ClassHolder classHolderSingle = this.classHolderSingle;
|
ClassHolder classHolderSingle = this.classHolderSingle;
|
||||||
StrongConcurrentSet<Class<?>> classes = classHolderSingle.get();
|
ConcurrentSet<Class<?>> classes = classHolderSingle.get();
|
||||||
|
|
||||||
StrongConcurrentSet<Class<?>> putIfAbsent = local.putIfAbsent(clazz, classes);
|
ConcurrentSet<Class<?>> putIfAbsent = local.putIfAbsent(clazz, classes);
|
||||||
if (putIfAbsent == null) {
|
if (putIfAbsent == null) {
|
||||||
// we are the first one in the map
|
// we are the first one in the map
|
||||||
classHolderSingle.set(classHolderSingle.initialValue());
|
classHolderSingle.set(classHolderSingle.initialValue());
|
||||||
|
|
||||||
// it doesn't matter if concurrent access stomps on values, since they are always the same.
|
// it doesn't matter if concurrent access stomps on values, since they are always the same.
|
||||||
StrongConcurrentSet<Class<?>> superTypes = ReflectionUtils.getSuperTypes(clazz);
|
Collection<Class<?>> superTypes = ReflectionUtils.getSuperTypes(clazz);
|
||||||
|
Iterator<Class<?>> iterator;
|
||||||
ISetEntry<Class<?>> current = superTypes.head;
|
|
||||||
Class<?> c;
|
Class<?> c;
|
||||||
while (current != null) {
|
|
||||||
c = current.getValue();
|
|
||||||
current = current.next();
|
|
||||||
|
|
||||||
|
for (iterator = superTypes.iterator(); iterator.hasNext();) {
|
||||||
|
c = iterator.next();
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
c = getArrayClass(c);
|
c = getArrayClass(c);
|
||||||
}
|
}
|
||||||
@ -111,4 +144,239 @@ public class SubscriptionUtils {
|
|||||||
this.superClassesCache.clear();
|
this.superClassesCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CAN NOT RETURN NULL
|
||||||
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
|
public final Collection<Subscription> getSuperSubscriptions(Class<?> superType) {
|
||||||
|
// whenever our subscriptions change, this map is cleared.
|
||||||
|
ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptions;
|
||||||
|
|
||||||
|
SubscriptionHolder subHolderConcurrent = this.subHolderConcurrent;
|
||||||
|
ConcurrentSet<Subscription> subsPerType = subHolderConcurrent.get();
|
||||||
|
|
||||||
|
// cache our subscriptions for super classes, so that their access can be fast!
|
||||||
|
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(superType, subsPerType);
|
||||||
|
if (putIfAbsent == null) {
|
||||||
|
// we are the first one in the map
|
||||||
|
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
||||||
|
|
||||||
|
Collection<Class<?>> types = getSuperClasses(superType);
|
||||||
|
if (types.isEmpty()) {
|
||||||
|
return subsPerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Class<?>, Collection<Subscription>> local2 = this.subscriptionsPerMessageSingle;
|
||||||
|
|
||||||
|
Class<?> superClass;
|
||||||
|
Iterator<Class<?>> iterator;
|
||||||
|
|
||||||
|
Iterator<Subscription> subIterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator = types.iterator(); iterator.hasNext();) {
|
||||||
|
superClass = iterator.next();
|
||||||
|
|
||||||
|
Collection<Subscription> subs = local2.get(superClass);
|
||||||
|
if (subs != null) {
|
||||||
|
for (subIterator = subs.iterator(); subIterator.hasNext();) {
|
||||||
|
sub = subIterator.next();
|
||||||
|
if (sub.acceptsSubtypes()) {
|
||||||
|
subsPerType.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subsPerType;
|
||||||
|
} else {
|
||||||
|
// someone beat us
|
||||||
|
return putIfAbsent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAN NOT RETURN NULL
|
||||||
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
|
public Collection<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2) {
|
||||||
|
HashMapTree<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptionsMulti;
|
||||||
|
|
||||||
|
// whenever our subscriptions change, this map is cleared.
|
||||||
|
HashMapTree<Class<?>, ConcurrentSet<Subscription>> subsPerTypeLeaf = local.getLeaf(superType1, superType2);
|
||||||
|
ConcurrentSet<Subscription> subsPerType = null;
|
||||||
|
|
||||||
|
// we DO NOT care about duplicate, because the answers will be the same
|
||||||
|
if (subsPerTypeLeaf != null) {
|
||||||
|
// if the leaf exists, then the value exists.
|
||||||
|
subsPerType = subsPerTypeLeaf.getValue();
|
||||||
|
} else {
|
||||||
|
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||||
|
subsPerType = subHolderSingle.get();
|
||||||
|
|
||||||
|
// cache our subscriptions for super classes, so that their access can be fast!
|
||||||
|
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2);
|
||||||
|
if (putIfAbsent == null) {
|
||||||
|
// we are the first one in the map
|
||||||
|
subHolderSingle.set(subHolderSingle.initialValue());
|
||||||
|
|
||||||
|
// whenever our subscriptions change, this map is cleared.
|
||||||
|
Collection<Class<?>> types1 = getSuperClasses(superType1);
|
||||||
|
|
||||||
|
if (types1 != null) {
|
||||||
|
Collection<Class<?>> types2 = getSuperClasses(superType2);
|
||||||
|
|
||||||
|
Collection<Subscription> subs;
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> leaf1;
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> leaf2;
|
||||||
|
|
||||||
|
Class<?> eventSuperType1;
|
||||||
|
Class<?> eventSuperType2;
|
||||||
|
|
||||||
|
Iterator<Class<?>> iterator1;
|
||||||
|
Iterator<Class<?>> iterator2;
|
||||||
|
|
||||||
|
Iterator<Subscription> subIterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator1 = types1.iterator(); iterator1.hasNext();) {
|
||||||
|
eventSuperType1 = iterator1.next();
|
||||||
|
|
||||||
|
boolean type1Matches = eventSuperType1 == superType1;
|
||||||
|
if (type1Matches) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf1 = this.subscriptionsPerMessageMulti.getLeaf(eventSuperType1);
|
||||||
|
if (leaf1 != null && types2 != null) {
|
||||||
|
for (iterator2 = types2.iterator(); iterator2.hasNext();) {
|
||||||
|
eventSuperType2 = iterator2.next();
|
||||||
|
|
||||||
|
if (type1Matches && eventSuperType2 == superType2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf2 = leaf1.getLeaf(eventSuperType2);
|
||||||
|
|
||||||
|
if (leaf2 != null) {
|
||||||
|
subs = leaf2.getValue();
|
||||||
|
if (subs != null) {
|
||||||
|
for (subIterator = subs.iterator(); subIterator.hasNext();) {
|
||||||
|
sub = subIterator.next();
|
||||||
|
if (sub.acceptsSubtypes()) {
|
||||||
|
subsPerType.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// someone beat us
|
||||||
|
subsPerType = putIfAbsent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subsPerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAN NOT RETURN NULL
|
||||||
|
// ALSO checks to see if the superClass accepts subtypes.
|
||||||
|
public Collection<Subscription> getSuperSubscriptions(Class<?> superType1, Class<?> superType2, Class<?> superType3) {
|
||||||
|
HashMapTree<Class<?>, ConcurrentSet<Subscription>> local = this.superClassSubscriptionsMulti;
|
||||||
|
|
||||||
|
// whenever our subscriptions change, this map is cleared.
|
||||||
|
HashMapTree<Class<?>, ConcurrentSet<Subscription>> subsPerTypeLeaf = local.getLeaf(superType1, superType2, superType3);
|
||||||
|
ConcurrentSet<Subscription> subsPerType;
|
||||||
|
|
||||||
|
|
||||||
|
// we DO NOT care about duplicate, because the answers will be the same
|
||||||
|
if (subsPerTypeLeaf != null) {
|
||||||
|
// if the leaf exists, then the value exists.
|
||||||
|
subsPerType = subsPerTypeLeaf.getValue();
|
||||||
|
} else {
|
||||||
|
SubscriptionHolder subHolderSingle = this.subHolderSingle;
|
||||||
|
subsPerType = subHolderSingle.get();
|
||||||
|
|
||||||
|
// cache our subscriptions for super classes, so that their access can be fast!
|
||||||
|
ConcurrentSet<Subscription> putIfAbsent = local.putIfAbsent(subsPerType, superType1, superType2, superType3);
|
||||||
|
if (putIfAbsent == null) {
|
||||||
|
// we are the first one in the map
|
||||||
|
subHolderSingle.set(subHolderSingle.initialValue());
|
||||||
|
|
||||||
|
Collection<Class<?>> types1 = getSuperClasses(superType1);
|
||||||
|
|
||||||
|
if (types1 != null) {
|
||||||
|
Collection<Class<?>> types2 = getSuperClasses(superType2);
|
||||||
|
Collection<Class<?>> types3 = getSuperClasses(superType3);
|
||||||
|
|
||||||
|
Collection<Subscription> subs;
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> leaf1;
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> leaf2;
|
||||||
|
HashMapTree<Class<?>, Collection<Subscription>> leaf3;
|
||||||
|
|
||||||
|
Class<?> eventSuperType1;
|
||||||
|
Class<?> eventSuperType2;
|
||||||
|
Class<?> eventSuperType3;
|
||||||
|
|
||||||
|
Iterator<Class<?>> iterator1;
|
||||||
|
Iterator<Class<?>> iterator2;
|
||||||
|
Iterator<Class<?>> iterator3;
|
||||||
|
|
||||||
|
Iterator<Subscription> subIterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator1 = types1.iterator(); iterator1.hasNext();) {
|
||||||
|
eventSuperType1 = iterator1.next();
|
||||||
|
|
||||||
|
boolean type1Matches = eventSuperType1 == superType1;
|
||||||
|
if (type1Matches) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf1 = this.subscriptionsPerMessageMulti.getLeaf(eventSuperType1);
|
||||||
|
if (leaf1 != null && types2 != null) {
|
||||||
|
for (iterator2 = types2.iterator(); iterator2.hasNext();) {
|
||||||
|
eventSuperType2 = iterator2.next();
|
||||||
|
|
||||||
|
boolean type12Matches = type1Matches && eventSuperType2 == superType2;
|
||||||
|
if (type12Matches) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf2 = leaf1.getLeaf(eventSuperType2);
|
||||||
|
|
||||||
|
if (leaf2 != null && types3 != null) {
|
||||||
|
for (iterator3 = types3.iterator(); iterator3.hasNext();) {
|
||||||
|
eventSuperType3 = iterator3.next();
|
||||||
|
|
||||||
|
if (type12Matches && eventSuperType3 == superType3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf3 = leaf2.getLeaf(eventSuperType3);
|
||||||
|
|
||||||
|
if (leaf3 != null) {
|
||||||
|
subs = leaf3.getValue();
|
||||||
|
if (subs != null) {
|
||||||
|
for (subIterator = subs.iterator(); subIterator.hasNext();) {
|
||||||
|
sub = subIterator.next();
|
||||||
|
if (sub.acceptsSubtypes()) {
|
||||||
|
subsPerType.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// someone beat us
|
||||||
|
subsPerType = putIfAbsent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subsPerType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package dorkbox.util.messagebus.common;
|
package dorkbox.util.messagebus.common;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@ -18,10 +20,10 @@ public class VarArgUtils {
|
|||||||
private final int stripeSize;
|
private final int stripeSize;
|
||||||
|
|
||||||
private final SubscriptionUtils utils;
|
private final SubscriptionUtils utils;
|
||||||
private final ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerMessageSingle;
|
private final Map<Class<?>, Collection<Subscription>> subscriptionsPerMessageSingle;
|
||||||
|
|
||||||
|
|
||||||
public VarArgUtils(SubscriptionUtils utils, ConcurrentMap<Class<?>, ConcurrentSet<Subscription>> subscriptionsPerMessageSingle,
|
public VarArgUtils(SubscriptionUtils utils, Map<Class<?>, Collection<Subscription>> subscriptionsPerMessageSingle,
|
||||||
float loadFactor, int stripeSize) {
|
float loadFactor, int stripeSize) {
|
||||||
|
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
@ -63,9 +65,13 @@ public class VarArgUtils {
|
|||||||
// this caches our array type. This is never cleared.
|
// this caches our array type. This is never cleared.
|
||||||
Class<?> arrayVersion = this.utils.getArrayClass(messageClass);
|
Class<?> arrayVersion = this.utils.getArrayClass(messageClass);
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subs = this.subscriptionsPerMessageSingle.get(arrayVersion);
|
Iterator<Subscription> iterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
Collection<Subscription> subs = this.subscriptionsPerMessageSingle.get(arrayVersion);
|
||||||
if (subs != null) {
|
if (subs != null) {
|
||||||
for (Subscription sub : subs) {
|
for (iterator = subs.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
if (sub.acceptsVarArgs()) {
|
if (sub.acceptsVarArgs()) {
|
||||||
subsPerType.add(sub);
|
subsPerType.add(sub);
|
||||||
}
|
}
|
||||||
@ -98,24 +104,27 @@ public class VarArgUtils {
|
|||||||
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
subHolderConcurrent.set(subHolderConcurrent.initialValue());
|
||||||
|
|
||||||
Class<?> arrayVersion = this.utils.getArrayClass(messageClass);
|
Class<?> arrayVersion = this.utils.getArrayClass(messageClass);
|
||||||
StrongConcurrentSet<Class<?>> types = this.utils.getSuperClasses(arrayVersion, true);
|
Collection<Class<?>> types = this.utils.getSuperClasses(arrayVersion, true);
|
||||||
if (types.isEmpty()) {
|
if (types.isEmpty()) {
|
||||||
return null;
|
return subsPerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Class<?>, ConcurrentSet<Subscription>> local2 = this.subscriptionsPerMessageSingle;
|
Map<Class<?>, Collection<Subscription>> local2 = this.subscriptionsPerMessageSingle;
|
||||||
|
|
||||||
ISetEntry<Class<?>> current1;
|
Iterator<Class<?>> iterator;
|
||||||
Class<?> superClass;
|
Class<?> superClass;
|
||||||
|
|
||||||
current1 = types.head;
|
Iterator<Subscription> subIterator;
|
||||||
while (current1 != null) {
|
Subscription sub;
|
||||||
superClass = current1.getValue();
|
|
||||||
current1 = current1.next();
|
|
||||||
|
|
||||||
ConcurrentSet<Subscription> subs = local2.get(superClass);
|
|
||||||
|
for (iterator = types.iterator(); iterator.hasNext();) {
|
||||||
|
superClass = iterator.next();
|
||||||
|
|
||||||
|
Collection<Subscription> subs = local2.get(superClass);
|
||||||
if (subs != null) {
|
if (subs != null) {
|
||||||
for (Subscription sub : subs) {
|
for (subIterator = subs.iterator(); subIterator.hasNext();) {
|
||||||
|
sub = subIterator.next();
|
||||||
if (sub.acceptsSubtypes() && sub.acceptsVarArgs()) {
|
if (sub.acceptsSubtypes() && sub.acceptsVarArgs()) {
|
||||||
subsPerType.add(sub);
|
subsPerType.add(sub);
|
||||||
}
|
}
|
||||||
@ -159,7 +168,11 @@ public class VarArgUtils {
|
|||||||
ConcurrentSet<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions(messageClass1);
|
ConcurrentSet<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions(messageClass1);
|
||||||
ConcurrentSet<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions(messageClass2);
|
ConcurrentSet<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions(messageClass2);
|
||||||
|
|
||||||
for (Subscription sub : varargSuperSubscriptions1) {
|
Iterator<Subscription> iterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator = varargSuperSubscriptions1.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
if (varargSuperSubscriptions2.contains(sub)) {
|
if (varargSuperSubscriptions2.contains(sub)) {
|
||||||
subsPerType.add(sub);
|
subsPerType.add(sub);
|
||||||
}
|
}
|
||||||
@ -202,7 +215,11 @@ public class VarArgUtils {
|
|||||||
ConcurrentSet<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions(messageClass2);
|
ConcurrentSet<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions(messageClass2);
|
||||||
ConcurrentSet<Subscription> varargSuperSubscriptions3 = getVarArgSuperSubscriptions(messageClass3);
|
ConcurrentSet<Subscription> varargSuperSubscriptions3 = getVarArgSuperSubscriptions(messageClass3);
|
||||||
|
|
||||||
for (Subscription sub : varargSuperSubscriptions1) {
|
Iterator<Subscription> iterator;
|
||||||
|
Subscription sub;
|
||||||
|
|
||||||
|
for (iterator = varargSuperSubscriptions1.iterator(); iterator.hasNext();) {
|
||||||
|
sub = iterator.next();
|
||||||
if (varargSuperSubscriptions2.contains(sub) && varargSuperSubscriptions3.contains(sub)) {
|
if (varargSuperSubscriptions2.contains(sub) && varargSuperSubscriptions3.contains(sub)) {
|
||||||
subsPerType.add(sub);
|
subsPerType.add(sub);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ package dorkbox.util.messagebus.common;
|
|||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
|
import dorkbox.util.messagebus.common.thread.StampedLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implementation uses weak references to the elements. Iterators automatically perform cleanups of
|
* This implementation uses weak references to the elements. Iterators automatically perform cleanups of
|
||||||
@ -34,9 +35,10 @@ public class WeakConcurrentSet<T> extends AbstractConcurrentSet<T>{
|
|||||||
// until it finds the first entry whose value has not yet been garbage collected
|
// until it finds the first entry whose value has not yet been garbage collected
|
||||||
// the method assumes that the current element is already orphaned and will remove it
|
// the method assumes that the current element is already orphaned and will remove it
|
||||||
private void removeOrphans(){
|
private void removeOrphans(){
|
||||||
Lock writelock = WeakConcurrentSet.this.lock.writeLock();
|
|
||||||
|
StampedLock lock = WeakConcurrentSet.this.lock;
|
||||||
|
long stamp = lock.writeLock();
|
||||||
try{
|
try{
|
||||||
writelock.lock();
|
|
||||||
do {
|
do {
|
||||||
ISetEntry<T> orphaned = this.current;
|
ISetEntry<T> orphaned = this.current;
|
||||||
this.current = this.current.next();
|
this.current = this.current.next();
|
||||||
@ -44,7 +46,7 @@ public class WeakConcurrentSet<T> extends AbstractConcurrentSet<T>{
|
|||||||
} while(this.current != null && this.current.getValue() == null);
|
} while(this.current != null && this.current.getValue() == null);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
writelock.unlock();
|
lock.unlockWrite(stamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
package dorkbox.util.messagebus.common.thread;
|
package dorkbox.util.messagebus.common.thread;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
|
||||||
|
|
||||||
|
|
||||||
public class ClassHolder extends ThreadLocal<StrongConcurrentSetV8<Class<?>>> {
|
public class ClassHolder extends ThreadLocal<ConcurrentSet<Class<?>>> {
|
||||||
|
|
||||||
private final float loadFactor;
|
private final float loadFactor;
|
||||||
|
private final int stripeSize;
|
||||||
|
|
||||||
public ClassHolder(float loadFactor) {
|
public ClassHolder(float loadFactor, int stripeSize) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.loadFactor = loadFactor;
|
this.loadFactor = loadFactor;
|
||||||
|
this.stripeSize = stripeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StrongConcurrentSetV8<Class<?>> initialValue() {
|
public ConcurrentSet<Class<?>> initialValue() {
|
||||||
return new StrongConcurrentSetV8<Class<?>>(16, this.loadFactor);
|
return new ConcurrentSet<Class<?>>(16, this.loadFactor, this.stripeSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class ConcurrentSet<T> extends ConcurrentLinkedQueue2<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node<T> pred = null;
|
Node<T> pred = null;
|
||||||
for (Node<T> p = node; p != null; p = succ(p)) {
|
for (Node<T> p = this.head; p != null; p = succ(p)) {
|
||||||
T item = p.item;
|
T item = p.item;
|
||||||
if (item != null &&
|
if (item != null &&
|
||||||
element.equals(item) &&
|
element.equals(item) &&
|
||||||
|
1526
src/main/java/dorkbox/util/messagebus/common/thread/StampedLock.java
Normal file
1526
src/main/java/dorkbox/util/messagebus/common/thread/StampedLock.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,9 @@
|
|||||||
package dorkbox.util.messagebus.listener;
|
package dorkbox.util.messagebus.listener;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All instances of any class that defines at least one message handler (see @MessageHandler) are message listeners. Thus, a message
|
* All instances of any class that defines at least one message handler (see @MessageHandler) are message listeners. Thus, a message
|
||||||
@ -18,11 +21,11 @@ import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
|||||||
*/
|
*/
|
||||||
public class MessageListener {
|
public class MessageListener {
|
||||||
|
|
||||||
private final StrongConcurrentSetV8<MessageHandler> handlers;
|
private final Collection<MessageHandler> handlers;
|
||||||
private Class<?> listenerDefinition;
|
private Class<?> listenerDefinition;
|
||||||
|
|
||||||
public MessageListener(Class<?> listenerDefinition, int size) {
|
public MessageListener(Class<?> listenerDefinition, int size, float loadFactor, int stripeSize) {
|
||||||
this.handlers = new StrongConcurrentSetV8<MessageHandler>(size, 0.8F);
|
this.handlers = new ConcurrentSet<MessageHandler>(size, loadFactor, stripeSize);
|
||||||
this.listenerDefinition = listenerDefinition;
|
this.listenerDefinition = listenerDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +38,7 @@ public class MessageListener {
|
|||||||
return this.handlers.add(messageHandler);
|
return this.handlers.add(messageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StrongConcurrentSetV8<MessageHandler> getHandlers() {
|
public Collection<MessageHandler> getHandlers() {
|
||||||
return this.handlers;
|
return this.handlers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package dorkbox.util.messagebus.listener;
|
package dorkbox.util.messagebus.listener;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.annotations.Handler;
|
import dorkbox.util.messagebus.annotations.Handler;
|
||||||
import dorkbox.util.messagebus.common.ISetEntry;
|
|
||||||
import dorkbox.util.messagebus.common.ReflectionUtils;
|
import dorkbox.util.messagebus.common.ReflectionUtils;
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The meta data reader is responsible for parsing and validating message handler configurations.
|
* The meta data reader is responsible for parsing and validating message handler configurations.
|
||||||
@ -19,35 +20,31 @@ public class MetadataReader {
|
|||||||
|
|
||||||
// get all listeners defined by the given class (includes
|
// get all listeners defined by the given class (includes
|
||||||
// listeners defined in super classes)
|
// listeners defined in super classes)
|
||||||
public MessageListener getMessageListener(Class<?> target) {
|
public MessageListener getMessageListener(Class<?> target, float loadFactor, int stripeSize) {
|
||||||
|
|
||||||
// get all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
// get all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
||||||
StrongConcurrentSetV8<Method> allHandlers = ReflectionUtils.getMethods(target);
|
Collection<Method> allHandlers = ReflectionUtils.getMethods(target);
|
||||||
|
|
||||||
// retain only those that are at the bottom of their respective class hierarchy (deepest overriding method)
|
// retain only those that are at the bottom of their respective class hierarchy (deepest overriding method)
|
||||||
StrongConcurrentSetV8<Method> bottomMostHandlers = new StrongConcurrentSetV8<Method>(allHandlers.size(), 0.8F);
|
Collection<Method> bottomMostHandlers = new ConcurrentSet<Method>(allHandlers.size(), loadFactor, stripeSize);
|
||||||
|
Iterator<Method> iterator;
|
||||||
|
|
||||||
ISetEntry<Method> current = allHandlers.head;
|
|
||||||
Method handler;
|
Method handler;
|
||||||
while (current != null) {
|
|
||||||
handler = current.getValue();
|
for (iterator = allHandlers.iterator(); iterator.hasNext();) {
|
||||||
current = current.next();
|
handler = iterator.next();
|
||||||
|
|
||||||
if (!ReflectionUtils.containsOverridingMethod(allHandlers, handler)) {
|
if (!ReflectionUtils.containsOverridingMethod(allHandlers, handler)) {
|
||||||
bottomMostHandlers.add(handler);
|
bottomMostHandlers.add(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageListener listenerMetadata = new MessageListener(target, bottomMostHandlers.size());
|
MessageListener listenerMetadata = new MessageListener(target, bottomMostHandlers.size(), loadFactor, stripeSize);
|
||||||
|
|
||||||
// for each handler there will be no overriding method that specifies @Handler annotation
|
// for each handler there will be no overriding method that specifies @Handler annotation
|
||||||
// but an overriding method does inherit the listener configuration of the overwritten method
|
// but an overriding method does inherit the listener configuration of the overwritten method
|
||||||
|
|
||||||
current = bottomMostHandlers.head;
|
for (iterator = bottomMostHandlers.iterator(); iterator.hasNext();) {
|
||||||
while (current != null) {
|
handler = iterator.next();
|
||||||
handler = current.getValue();
|
|
||||||
current = current.next();
|
|
||||||
|
|
||||||
Handler handlerConfig = ReflectionUtils.getAnnotation(handler, Handler.class);
|
Handler handlerConfig = ReflectionUtils.getAnnotation(handler, Handler.class);
|
||||||
if (handlerConfig == null || !handlerConfig.enabled()) {
|
if (handlerConfig == null || !handlerConfig.enabled()) {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package dorkbox.util.messagebus.subscription;
|
package dorkbox.util.messagebus.subscription;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.ISetEntry;
|
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSet;
|
|
||||||
import dorkbox.util.messagebus.common.thread.BooleanHolder;
|
import dorkbox.util.messagebus.common.thread.BooleanHolder;
|
||||||
|
import dorkbox.util.messagebus.common.thread.ConcurrentSet;
|
||||||
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;
|
||||||
@ -39,11 +39,11 @@ public class Subscription {
|
|||||||
private final MessageHandler handlerMetadata;
|
private final MessageHandler handlerMetadata;
|
||||||
|
|
||||||
private final IHandlerInvocation invocation;
|
private final IHandlerInvocation invocation;
|
||||||
private final StrongConcurrentSet<Object> listeners;
|
private final ConcurrentSet<Object> listeners;
|
||||||
|
|
||||||
public Subscription(MessageHandler handler) {
|
public Subscription(MessageHandler handler) {
|
||||||
this.handlerMetadata = handler;
|
this.handlerMetadata = handler;
|
||||||
this.listeners = new StrongConcurrentSet<Object>();
|
this.listeners = new ConcurrentSet<Object>();
|
||||||
|
|
||||||
IHandlerInvocation invocation = new ReflectiveHandlerInvocation();
|
IHandlerInvocation invocation = new ReflectiveHandlerInvocation();
|
||||||
if (handler.isSynchronized()) {
|
if (handler.isSynchronized()) {
|
||||||
@ -93,27 +93,22 @@ public class Subscription {
|
|||||||
return this.listeners.size();
|
return this.listeners.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int count =0;
|
|
||||||
public int getCount() {
|
|
||||||
return this.count;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @return true if there were listeners for this publication, false if there was nothing
|
* @return true if there were listeners for this publication, false if there was nothing
|
||||||
*/
|
*/
|
||||||
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message) {
|
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message) {
|
||||||
StrongConcurrentSet<Object> listeners = this.listeners;
|
ConcurrentSet<Object> listeners = this.listeners;
|
||||||
|
|
||||||
if (!listeners.isEmpty()) {
|
if (!listeners.isEmpty()) {
|
||||||
MethodAccess handler = this.handlerMetadata.getHandler();
|
MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
int handleIndex = this.handlerMetadata.getMethodIndex();
|
int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
IHandlerInvocation invocation = this.invocation;
|
IHandlerInvocation invocation = this.invocation;
|
||||||
|
|
||||||
|
Iterator<Object> iterator;
|
||||||
ISetEntry<Object> current = listeners.head;
|
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
|
||||||
listener = current.getValue();
|
for (iterator = listeners.iterator(); iterator.hasNext();) {
|
||||||
current = current.next();
|
listener = iterator.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
invocation.invoke(listener, handler, handleIndex, message);
|
invocation.invoke(listener, handler, handleIndex, message);
|
||||||
@ -160,128 +155,128 @@ public class Subscription {
|
|||||||
* @return true if there were listeners for this publication, false if there was nothing
|
* @return true if there were listeners for this publication, false if there was nothing
|
||||||
*/
|
*/
|
||||||
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2) {
|
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2) {
|
||||||
StrongConcurrentSet<Object> listeners = this.listeners;
|
// StrongConcurrentSet<Object> listeners = this.listeners;
|
||||||
|
//
|
||||||
if (!listeners.isEmpty()) {
|
// if (!listeners.isEmpty()) {
|
||||||
MethodAccess handler = this.handlerMetadata.getHandler();
|
// MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
int handleIndex = this.handlerMetadata.getMethodIndex();
|
// int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
IHandlerInvocation invocation = this.invocation;
|
// IHandlerInvocation invocation = this.invocation;
|
||||||
|
//
|
||||||
|
//
|
||||||
ISetEntry<Object> current = listeners.head;
|
// ISetEntry<Object> current = listeners.head;
|
||||||
Object listener;
|
// Object listener;
|
||||||
while (current != null) {
|
// while (current != null) {
|
||||||
listener = current.getValue();
|
// listener = current.getValue();
|
||||||
current = current.next();
|
// current = current.next();
|
||||||
|
//
|
||||||
try {
|
// try {
|
||||||
invocation.invoke(listener, handler, handleIndex, message1, message2);
|
// invocation.invoke(listener, handler, handleIndex, message1, message2);
|
||||||
} catch (IllegalAccessException e) {
|
// } catch (IllegalAccessException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"The class or method is not accessible")
|
// "The class or method is not accessible")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2));
|
// .setPublishedObject(message1, message2));
|
||||||
} catch (IllegalArgumentException e) {
|
// } catch (IllegalArgumentException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"Wrong arguments passed to method. Was: " +
|
// "Wrong arguments passed to method. Was: " +
|
||||||
message1.getClass() + ", " +
|
// message1.getClass() + ", " +
|
||||||
message2.getClass()
|
// message2.getClass()
|
||||||
+ ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
// + ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
||||||
handler.getParameterTypes()[1]
|
// handler.getParameterTypes()[1]
|
||||||
)
|
// )
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2));
|
// .setPublishedObject(message1, message2));
|
||||||
} catch (InvocationTargetException e) {
|
// } catch (InvocationTargetException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"Message handler threw exception")
|
// "Message handler threw exception")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2));
|
// .setPublishedObject(message1, message2));
|
||||||
} catch (Throwable e) {
|
// } catch (Throwable e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"The handler code threw an exception")
|
// "The handler code threw an exception")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2));
|
// .setPublishedObject(message1, message2));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
booleanHolder.bool = true;
|
// booleanHolder.bool = true;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if there were listeners for this publication, false if there was nothing
|
* @return true if there were listeners for this publication, false if there was nothing
|
||||||
*/
|
*/
|
||||||
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2, Object message3) {
|
public void publishToSubscription(ErrorHandlingSupport errorHandler, BooleanHolder booleanHolder, Object message1, Object message2, Object message3) {
|
||||||
StrongConcurrentSet<Object> listeners = this.listeners;
|
// StrongConcurrentSet<Object> listeners = this.listeners;
|
||||||
|
//
|
||||||
if (!listeners.isEmpty()) {
|
// if (!listeners.isEmpty()) {
|
||||||
MethodAccess handler = this.handlerMetadata.getHandler();
|
// MethodAccess handler = this.handlerMetadata.getHandler();
|
||||||
int handleIndex = this.handlerMetadata.getMethodIndex();
|
// int handleIndex = this.handlerMetadata.getMethodIndex();
|
||||||
IHandlerInvocation invocation = this.invocation;
|
// IHandlerInvocation invocation = this.invocation;
|
||||||
|
//
|
||||||
|
//
|
||||||
ISetEntry<Object> current = listeners.head;
|
// ISetEntry<Object> current = listeners.head;
|
||||||
Object listener;
|
// Object listener;
|
||||||
while (current != null) {
|
// while (current != null) {
|
||||||
listener = current.getValue();
|
// listener = current.getValue();
|
||||||
current = current.next();
|
// current = current.next();
|
||||||
|
//
|
||||||
try {
|
// try {
|
||||||
invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
// invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
||||||
} catch (IllegalAccessException e) {
|
// } catch (IllegalAccessException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"The class or method is not accessible")
|
// "The class or method is not accessible")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2, message3));
|
// .setPublishedObject(message1, message2, message3));
|
||||||
} catch (IllegalArgumentException e) {
|
// } catch (IllegalArgumentException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"Wrong arguments passed to method. Was: " +
|
// "Wrong arguments passed to method. Was: " +
|
||||||
message1.getClass() + ", " +
|
// message1.getClass() + ", " +
|
||||||
message2.getClass() + ", " +
|
// message2.getClass() + ", " +
|
||||||
message3.getClass()
|
// message3.getClass()
|
||||||
+ ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
// + ". Expected: " + handler.getParameterTypes()[0] + ", " +
|
||||||
handler.getParameterTypes()[1] + ", " +
|
// handler.getParameterTypes()[1] + ", " +
|
||||||
handler.getParameterTypes()[2]
|
// handler.getParameterTypes()[2]
|
||||||
)
|
// )
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2, message3));
|
// .setPublishedObject(message1, message2, message3));
|
||||||
} catch (InvocationTargetException e) {
|
// } catch (InvocationTargetException e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"Message handler threw exception")
|
// "Message handler threw exception")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2, message3));
|
// .setPublishedObject(message1, message2, message3));
|
||||||
} catch (Throwable e) {
|
// } catch (Throwable e) {
|
||||||
errorHandler.handlePublicationError(new PublicationError()
|
// errorHandler.handlePublicationError(new PublicationError()
|
||||||
.setMessage("Error during invocation of message handler. " +
|
// .setMessage("Error during invocation of message handler. " +
|
||||||
"The handler code threw an exception")
|
// "The handler code threw an exception")
|
||||||
.setCause(e)
|
// .setCause(e)
|
||||||
.setMethodName(handler.getMethodNames()[handleIndex])
|
// .setMethodName(handler.getMethodNames()[handleIndex])
|
||||||
.setListener(listener)
|
// .setListener(listener)
|
||||||
.setPublishedObject(message1, message2, message3));
|
// .setPublishedObject(message1, message2, message3));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
booleanHolder.bool = true;
|
// booleanHolder.bool = true;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ public class MetadataReaderTest extends AssertSupport {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListenerWithoutInheritance() {
|
public void testListenerWithoutInheritance() {
|
||||||
MessageListener listener = this.reader.getMessageListener(MessageListener1.class);
|
MessageListener listener = this.reader.getMessageListener(MessageListener1.class, 0.85F, 4);
|
||||||
ListenerValidator validator = new ListenerValidator()
|
ListenerValidator validator = new ListenerValidator()
|
||||||
.expectHandlers(2, String.class)
|
.expectHandlers(2, String.class)
|
||||||
.expectHandlers(2, Object.class)
|
.expectHandlers(2, Object.class)
|
||||||
@ -45,7 +45,7 @@ public class MetadataReaderTest extends AssertSupport {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListenerWithInheritance() {
|
public void testListenerWithInheritance() {
|
||||||
MessageListener listener = this.reader.getMessageListener(MessageListener2.class);
|
MessageListener listener = this.reader.getMessageListener(MessageListener2.class, 0.85F, 4);
|
||||||
ListenerValidator validator = new ListenerValidator()
|
ListenerValidator validator = new ListenerValidator()
|
||||||
.expectHandlers(2, String.class)
|
.expectHandlers(2, String.class)
|
||||||
.expectHandlers(2, Object.class)
|
.expectHandlers(2, Object.class)
|
||||||
@ -55,7 +55,7 @@ public class MetadataReaderTest extends AssertSupport {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListenerWithInheritanceOverriding() {
|
public void testListenerWithInheritanceOverriding() {
|
||||||
MessageListener listener = this.reader.getMessageListener(MessageListener3.class);
|
MessageListener listener = this.reader.getMessageListener(MessageListener3.class, 0.85F, 4);
|
||||||
|
|
||||||
ListenerValidator validator = new ListenerValidator()
|
ListenerValidator validator = new ListenerValidator()
|
||||||
.expectHandlers(0, String.class)
|
.expectHandlers(0, String.class)
|
||||||
@ -198,7 +198,7 @@ public class MetadataReaderTest extends AssertSupport {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleSignatureListenerWithoutInheritance() {
|
public void testMultipleSignatureListenerWithoutInheritance() {
|
||||||
MessageListener listener = this.reader.getMessageListener(MultiMessageListener1.class);
|
MessageListener listener = this.reader.getMessageListener(MultiMessageListener1.class, 0.85F, 4);
|
||||||
ListenerValidator validator = new ListenerValidator()
|
ListenerValidator validator = new ListenerValidator()
|
||||||
.expectHandlers(7, String.class)
|
.expectHandlers(7, String.class)
|
||||||
.expectHandlers(9, String.class, String.class)
|
.expectHandlers(9, String.class, String.class)
|
||||||
|
@ -27,11 +27,12 @@ public class PerfTest_Collections {
|
|||||||
public static final Integer TEST_VALUE = Integer.valueOf(777);
|
public static final Integer TEST_VALUE = Integer.valueOf(777);
|
||||||
|
|
||||||
private static final float LOAD_FACTOR = 0.8F;
|
private static final float LOAD_FACTOR = 0.8F;
|
||||||
private static MessageListener messageListener = new MetadataReader().getMessageListener(Listener.class);
|
private static final MessageListener messageListener = new MetadataReader().getMessageListener(Listener.class, LOAD_FACTOR, 8);
|
||||||
|
|
||||||
public static void main(final String[] args) throws Exception {
|
public static void main(final String[] args) throws Exception {
|
||||||
final int size = 16;
|
final int size = 16;
|
||||||
|
|
||||||
|
|
||||||
System.out.println("reps:" + REPETITIONS + " size: " + size);
|
System.out.println("reps:" + REPETITIONS + " size: " + size);
|
||||||
|
|
||||||
// have to warm-up the JVM.
|
// have to warm-up the JVM.
|
||||||
@ -72,8 +73,10 @@ public class PerfTest_Collections {
|
|||||||
final int warmupRuns = 2;
|
final int warmupRuns = 2;
|
||||||
final int runs = 3;
|
final int runs = 3;
|
||||||
|
|
||||||
|
Collection<MessageHandler> handlers = messageListener.getHandlers();
|
||||||
|
|
||||||
for (int i=0;i<size;i++) {
|
for (int i=0;i<size;i++) {
|
||||||
for (MessageHandler x : messageListener.getHandlers()) {
|
for (MessageHandler x : handlers) {
|
||||||
set.add(new Subscription(x));
|
set.add(new Subscription(x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user