Cleaned up multi-subs and multi-super-subs
This commit is contained in:
parent
c323e29287
commit
7e262d1f0c
@ -118,7 +118,7 @@ class MessageBus implements IMessageBus {
|
|||||||
/**
|
/**
|
||||||
* Will subscribe and publish using all provided parameters in the method signature (for subscribe), and arguments (for publish)
|
* Will subscribe and publish using all provided parameters in the method signature (for subscribe), and arguments (for publish)
|
||||||
*/
|
*/
|
||||||
this.subscriptionManager = new SubscriptionManager(numberOfThreads, errorHandler);
|
this.subscriptionManager = new SubscriptionManager(numberOfThreads);
|
||||||
|
|
||||||
switch (publishMode) {
|
switch (publishMode) {
|
||||||
case Exact:
|
case Exact:
|
||||||
|
@ -35,9 +35,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
* Date: 2/2/15
|
* Date: 2/2/15
|
||||||
*/
|
*/
|
||||||
public class ClassTree<KEY> {
|
public class ClassTree<KEY> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static int INITIAL_SIZE = 4;
|
public static int INITIAL_SIZE = 4;
|
||||||
public static float LOAD_FACTOR = 0.8F;
|
public static float LOAD_FACTOR = 0.8F;
|
||||||
|
|
||||||
|
@ -44,12 +44,13 @@ class PublisherExact implements Publisher {
|
|||||||
|
|
||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
synchrony.publish(subscriptions, message1);
|
synchrony.publish(subscriptions, message1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Dead Event must EXACTLY MATCH (no subclasses)
|
// Dead Event must EXACTLY MATCH (no subclasses)
|
||||||
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
||||||
|
|
||||||
if (deadSubscriptions != null) {
|
if (deadSubscriptions != null) {
|
||||||
synchrony.publish(deadSubscriptions, new DeadMessage(message1));
|
synchrony.publish(deadSubscriptions, new DeadMessage(message1));
|
||||||
}
|
}
|
||||||
@ -72,12 +73,13 @@ class PublisherExact implements Publisher {
|
|||||||
|
|
||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
synchrony.publish(subscriptions, message1, message2);
|
synchrony.publish(subscriptions, message1, message2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Dead Event must EXACTLY MATCH (no subclasses)
|
// Dead Event must EXACTLY MATCH (no subclasses)
|
||||||
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
||||||
|
|
||||||
if (deadSubscriptions != null) {
|
if (deadSubscriptions != null) {
|
||||||
synchrony.publish(deadSubscriptions, new DeadMessage(message1, message2));
|
synchrony.publish(deadSubscriptions, new DeadMessage(message1, message2));
|
||||||
}
|
}
|
||||||
@ -101,12 +103,13 @@ class PublisherExact implements Publisher {
|
|||||||
|
|
||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
synchrony.publish(subscriptions, message1, message2, message3);
|
synchrony.publish(subscriptions, message1, message2, message3);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Dead Event must EXACTLY MATCH (no subclasses)
|
// Dead Event must EXACTLY MATCH (no subclasses)
|
||||||
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
final Subscription[] deadSubscriptions = subManager.getSubs(DeadMessage.class); // can return null
|
||||||
|
|
||||||
if (deadSubscriptions != null) {
|
if (deadSubscriptions != null) {
|
||||||
synchrony.publish(deadSubscriptions, new DeadMessage(message1, message2, message3));
|
synchrony.publish(deadSubscriptions, new DeadMessage(message1, message2, message3));
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,8 @@ class PublisherExactWithSuperTypes implements Publisher {
|
|||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
final Subscription[] subscriptions = subManager.getSubs(message1Class); // can return null
|
final Subscription[] subscriptions = subManager.getSubs(message1Class); // can return null
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(subscriptions, message1);
|
synchrony.publish(subscriptions, message1);
|
||||||
}
|
}
|
||||||
@ -54,6 +56,8 @@ class PublisherExactWithSuperTypes implements Publisher {
|
|||||||
// Run superSubscriptions
|
// Run superSubscriptions
|
||||||
final Subscription[] superSubscriptions = subManager.getSuperSubs(message1Class); // NOT return null
|
final Subscription[] superSubscriptions = subManager.getSuperSubs(message1Class); // NOT return null
|
||||||
if (superSubscriptions.length > 0) {
|
if (superSubscriptions.length > 0) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(superSubscriptions, message1);
|
synchrony.publish(superSubscriptions, message1);
|
||||||
}
|
}
|
||||||
@ -86,14 +90,18 @@ class PublisherExactWithSuperTypes implements Publisher {
|
|||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
final Subscription[] subscriptions = subManager.getSubs(messageClass1, messageClass2); // can return null
|
final Subscription[] subscriptions = subManager.getSubs(messageClass1, messageClass2); // can return null
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(subscriptions, message1, message2);
|
synchrony.publish(subscriptions, message1, message2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Run superSubscriptions
|
// Run superSubscriptions
|
||||||
final Subscription[] superSubscriptions = subManager.getSuperSubs(messageClass1, messageClass2); // can return null
|
final Subscription[] superSubscriptions = subManager.getSuperSubs(messageClass1, messageClass2); // NOT return null
|
||||||
if (superSubscriptions != null) {
|
if (superSubscriptions.length > 0) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(superSubscriptions, message1, message2);
|
synchrony.publish(superSubscriptions, message1, message2);
|
||||||
}
|
}
|
||||||
@ -127,14 +135,18 @@ class PublisherExactWithSuperTypes implements Publisher {
|
|||||||
// Run subscriptions
|
// Run subscriptions
|
||||||
final Subscription[] subscriptions = subManager.getSubs(messageClass1, messageClass2, messageClass3); // can return null
|
final Subscription[] subscriptions = subManager.getSubs(messageClass1, messageClass2, messageClass3); // can return null
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(subscriptions, message1, message2, message3);
|
synchrony.publish(subscriptions, message1, message2, message3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Run superSubscriptions
|
// Run superSubscriptions
|
||||||
final Subscription[] superSubscriptions = subManager.getSuperSubs(messageClass1, messageClass2, messageClass3); // can return null
|
final Subscription[] superSubscriptions = subManager.getSuperSubs(messageClass1, messageClass2, messageClass3); // NOT return null
|
||||||
if (superSubscriptions != null) {
|
if (superSubscriptions.length > 0) {
|
||||||
|
// this can only tell if we have subscribed at some point -- but not if we currently have anything (because of the async
|
||||||
|
// nature of publication
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
synchrony.publish(superSubscriptions, message1, message2, message3);
|
synchrony.publish(superSubscriptions, message1, message2, message3);
|
||||||
}
|
}
|
||||||
|
@ -147,19 +147,19 @@ class Subscription {
|
|||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public abstract
|
public abstract
|
||||||
boolean publish(final Object message) throws Throwable;
|
void publish(final Object message) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public abstract
|
public abstract
|
||||||
boolean publish(final Object message1, final Object message2) throws Throwable;
|
void publish(final Object message1, final Object message2) throws Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public abstract
|
public abstract
|
||||||
boolean publish(final Object message1, final Object message2, final Object message3) throws Throwable;
|
void publish(final Object message1, final Object message2, final Object message3) throws Throwable;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,11 +20,9 @@ import dorkbox.util.messagebus.MessageBus;
|
|||||||
import dorkbox.util.messagebus.common.ClassTree;
|
import dorkbox.util.messagebus.common.ClassTree;
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
import dorkbox.util.messagebus.common.MultiClass;
|
import dorkbox.util.messagebus.common.MultiClass;
|
||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|
||||||
import dorkbox.util.messagebus.subscription.asm.SubMakerAsm;
|
import dorkbox.util.messagebus.subscription.asm.SubMakerAsm;
|
||||||
import dorkbox.util.messagebus.subscription.reflection.SubMakerReflection;
|
import dorkbox.util.messagebus.subscription.reflection.SubMakerReflection;
|
||||||
import dorkbox.util.messagebus.utils.ClassUtils;
|
import dorkbox.util.messagebus.utils.ClassUtils;
|
||||||
import dorkbox.util.messagebus.utils.SubscriptionUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -41,12 +39,11 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
|||||||
* @author dorkbox, llc
|
* @author dorkbox, llc
|
||||||
* Date: 2/2/15
|
* Date: 2/2/15
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({"unchecked", "ToArrayCallWithZeroLengthArrayArgument"})
|
||||||
public final
|
public final
|
||||||
class SubscriptionManager {
|
class SubscriptionManager {
|
||||||
public static final float LOAD_FACTOR = 0.8F;
|
public static final float LOAD_FACTOR = 0.8F;
|
||||||
|
public static final Subscription[] EMPTY_SUBS = new Subscription[0];
|
||||||
// TODO: during startup, pre-calculate the number of subscription listeners and x2 to save as subsPerListener expected max size
|
|
||||||
|
|
||||||
// controls if we use java reflection or ASM to access methods during publication
|
// controls if we use java reflection or ASM to access methods during publication
|
||||||
private final SubMaker subMaker;
|
private final SubMaker subMaker;
|
||||||
@ -70,6 +67,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// keeps track of all subscriptions of the super classes of a message type.
|
// keeps track of all subscriptions of the super classes of a message type.
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperSingle;
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperSingle;
|
||||||
|
private volatile IdentityMap<MultiClass, Subscription[]> subsSuperMulti;
|
||||||
|
|
||||||
// In order to force the "Single writer principle" on subscribe & unsubscribe, they are within WRITE LOCKS. They could be dispatched
|
// In order to force the "Single writer principle" on subscribe & unsubscribe, they are within WRITE LOCKS. They could be dispatched
|
||||||
// to another thread, however we do NOT want them asynchronous - as publish() should ALWAYS succeed if a correct subscribe() is
|
// to another thread, however we do NOT want them asynchronous - as publish() should ALWAYS succeed if a correct subscribe() is
|
||||||
@ -78,11 +76,6 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final ErrorHandlingSupport errorHandler;
|
|
||||||
|
|
||||||
private final SubscriptionUtils subUtils;
|
|
||||||
|
|
||||||
private final ClassTree<Class<?>> classTree;
|
private final ClassTree<Class<?>> classTree;
|
||||||
|
|
||||||
private final ClassUtils classUtils;
|
private final ClassUtils classUtils;
|
||||||
@ -105,14 +98,13 @@ class SubscriptionManager {
|
|||||||
IdentityMap.class,
|
IdentityMap.class,
|
||||||
"subsSuperSingle");
|
"subsSuperSingle");
|
||||||
|
|
||||||
//NOTE for multiArg, can use the memory address concatenated with other ones and then just put it in the 'single" map (convert single to
|
private static final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSuperMultiREF =
|
||||||
// use this too). it would likely have to be longs no idea what to do for arrays?? (arrays should verify all the elements are the
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
// correct type too)
|
IdentityMap.class,
|
||||||
|
"subsSuperSingle");
|
||||||
|
|
||||||
public
|
public
|
||||||
SubscriptionManager(final int numberOfThreads, final ErrorHandlingSupport errorHandler) {
|
SubscriptionManager(final int numberOfThreads) {
|
||||||
this.errorHandler = errorHandler;
|
|
||||||
|
|
||||||
if (MessageBus.useAsmForDispatch) {
|
if (MessageBus.useAsmForDispatch) {
|
||||||
this.subMaker = new SubMakerAsm();
|
this.subMaker = new SubMakerAsm();
|
||||||
}
|
}
|
||||||
@ -130,12 +122,15 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
|
|
||||||
subsSuperSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSuperSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
subsSuperMulti = new IdentityMap<MultiClass, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
|
||||||
|
|
||||||
classTree = new ClassTree<Class<?>>();
|
classTree = new ClassTree<Class<?>>();
|
||||||
subUtils = new SubscriptionUtils(classUtils, LOAD_FACTOR, numberOfThreads);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down and clears all memory usage by the subscriptions
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
|
|
||||||
@ -164,6 +159,15 @@ class SubscriptionManager {
|
|||||||
this.classUtils.shutdown();
|
this.classUtils.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes a specific listener. The infrastructure for subscription never "shrinks", meaning that when a listener is un-subscribed,
|
||||||
|
* the listeners are only removed from the internal map -- the map itself is not cleaned up until a 'shutdown' is called.
|
||||||
|
*
|
||||||
|
* This method uses the "single-writer-principle" for lock-free publication. Since there are only 2
|
||||||
|
* methods to guarantee this method can only be called one-at-a-time (either it is only called by one thread, or only one thread can
|
||||||
|
* access it at a time) -- we chose the 2nd option -- and use a 'synchronized' block to make sure that only one thread can access
|
||||||
|
* this method at a time.
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
void subscribe(final Object listener) {
|
void subscribe(final Object listener) {
|
||||||
final Class<?> listenerClass = listener.getClass();
|
final Class<?> listenerClass = listener.getClass();
|
||||||
@ -340,6 +344,18 @@ class SubscriptionManager {
|
|||||||
// activates this sub for sub/unsub (only used by the subscription writer thread)
|
// activates this sub for sub/unsub (only used by the subscription writer thread)
|
||||||
subsPerListener.put(listenerClass, subscriptions);
|
subsPerListener.put(listenerClass, subscriptions);
|
||||||
|
|
||||||
|
|
||||||
|
// dump the super subscriptions
|
||||||
|
IdentityMap<Class<?>, Subscription[]> superSingleSubs = subsSuperSingleREF.get(this);
|
||||||
|
superSingleSubs.clear();
|
||||||
|
subsSuperSingleREF.lazySet((this), superSingleSubs);
|
||||||
|
|
||||||
|
IdentityMap<MultiClass, Subscription[]> superMultiSubs = subsSuperMultiREF.get(this);
|
||||||
|
superMultiSubs.clear();
|
||||||
|
subsSuperMultiREF.lazySet((this), superMultiSubs);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// save this snapshot back to the original (single writer principle)
|
// save this snapshot back to the original (single writer principle)
|
||||||
subsSingleREF.lazySet(this, singleSubs);
|
subsSingleREF.lazySet(this, singleSubs);
|
||||||
subsMultiREF.lazySet(this, multiSubs);
|
subsMultiREF.lazySet(this, multiSubs);
|
||||||
@ -362,6 +378,16 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un-subscribes a specific listener. The infrastructure for subscription never "shrinks", meaning that when a listener is un-subscribed,
|
||||||
|
* the listeners are only removed from the internal map -- the map itself is not cleaned up until a 'shutdown' is called.
|
||||||
|
*
|
||||||
|
* This method uses the "single-writer-principle" for lock-free publication. Since there are only 2
|
||||||
|
* methods to guarantee this method can only be called one-at-a-time (either it is only called by one thread, or only one thread can
|
||||||
|
* access it at a time) -- we chose the 2nd option -- and use a 'synchronized' block to make sure that only one thread can access
|
||||||
|
* this method at a time.
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
void unsubscribe(final Object listener) {
|
void unsubscribe(final Object listener) {
|
||||||
final Class<?> listenerClass = listener.getClass();
|
final Class<?> listenerClass = listener.getClass();
|
||||||
@ -387,75 +413,19 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private
|
|
||||||
void registerExtraSubs(final Class<?> clazz,
|
|
||||||
final IdentityMap<Class<?>, Subscription[]> subsPerMessageSingle,
|
|
||||||
final IdentityMap<Class<?>, Subscription[]> subsPerSuperMessageSingle,
|
|
||||||
final IdentityMap<Class<?>, Subscription[]> subsPerVarityMessageSingle) {
|
|
||||||
|
|
||||||
// final Class<?> arrayVersion = this.classUtils.getArrayClass(clazz); // never returns null, cached response
|
/**
|
||||||
final Class<?>[] superClasses = this.classUtils.getSuperClasses(clazz); // never returns null, cached response
|
* @return can return null
|
||||||
|
*/
|
||||||
Subscription sub;
|
|
||||||
//
|
|
||||||
// // Register Varity (Var-Arg) subscriptions
|
|
||||||
// final Subscription[] arraySubs = subsPerMessageSingle.get(arrayVersion);
|
|
||||||
// if (arraySubs != null) {
|
|
||||||
// final int length = arraySubs.length;
|
|
||||||
// final ArrayList<Subscription> varArgSubsAsList = new ArrayList<Subscription>(length);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// sub = arraySubs[i];
|
|
||||||
//
|
|
||||||
// if (sub.getHandlerAccess().acceptsVarArgs()) {
|
|
||||||
// varArgSubsAsList.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!varArgSubsAsList.isEmpty()) {
|
|
||||||
// subsPerVarityMessageSingle.put(clazz, varArgSubsAsList.toArray(new Subscription[0]));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// // Register SuperClass subscriptions
|
|
||||||
// final int length = superClasses.length;
|
|
||||||
// final ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length);
|
|
||||||
//
|
|
||||||
// // walks through all of the subscriptions that might exist for super types, and if applicable, save them
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// final Class<?> superClass = superClasses[i];
|
|
||||||
// final Subscription[] superSubs = subsPerMessageSingle.get(superClass);
|
|
||||||
//
|
|
||||||
// if (superSubs != null) {
|
|
||||||
// int superSubLength = superSubs.length;
|
|
||||||
// for (int j = 0; j < superSubLength; j++) {
|
|
||||||
// sub = superSubs[j];
|
|
||||||
//
|
|
||||||
// if (sub.getHandlerAccess().acceptsSubtypes()) {
|
|
||||||
// subsAsList.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!subsAsList.isEmpty()) {
|
|
||||||
// // save the subscriptions
|
|
||||||
// subsPerSuperMessageSingle.put(clazz, subsAsList.toArray(new Subscription[0]));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
public
|
public
|
||||||
Subscription[] getSubs(final Class<?> messageClass) {
|
Subscription[] getSubs(final Class<?> messageClass) {
|
||||||
return (Subscription[]) subsSingleREF.get(this).get(messageClass);
|
return (Subscription[]) subsSingleREF.get(this).get(messageClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
|
||||||
|
/**
|
||||||
|
* @return can return null
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
// never returns null
|
// never returns null
|
||||||
@ -464,7 +434,9 @@ class SubscriptionManager {
|
|||||||
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
/**
|
||||||
|
* @return can return null
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
// never returns null
|
// never returns null
|
||||||
@ -474,17 +446,9 @@ class SubscriptionManager {
|
|||||||
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
/**
|
||||||
public
|
* @return can NOT return null
|
||||||
Subscription[] getSubs(final Class<?>[] messageClasses) {
|
*/
|
||||||
// never returns null
|
|
||||||
final MultiClass multiClass = classTree.get(messageClasses);
|
|
||||||
|
|
||||||
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// can NOT return null
|
|
||||||
public
|
public
|
||||||
Subscription[] getSuperSubs(final Class<?> messageClass) {
|
Subscription[] getSuperSubs(final Class<?> messageClass) {
|
||||||
// The subscriptions that are remembered here DO NOT CHANGE (only the listeners inside them change).
|
// The subscriptions that are remembered here DO NOT CHANGE (only the listeners inside them change).
|
||||||
@ -494,7 +458,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
Subscription[] subscriptions = localSuperSubs.get(messageClass);
|
Subscription[] subscriptions = localSuperSubs.get(messageClass);
|
||||||
// the only time this is null, is when subscriptions DO NOT exist, and they haven't been calculated. Otherwise, if they are
|
// the only time this is null, is when subscriptions DO NOT exist, and they haven't been calculated. Otherwise, if they are
|
||||||
// calculated and do not exist - this will be an empty array.
|
// calculated and if they do not exist - this will be an empty array.
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
final Class<?>[] superClasses = this.classUtils.getSuperClasses(messageClass); // never returns null, cached response
|
final Class<?>[] superClasses = this.classUtils.getSuperClasses(messageClass); // never returns null, cached response
|
||||||
|
|
||||||
@ -525,7 +489,7 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// subsAsList now contains ALL of the super-class subscriptions.
|
// subsAsList now contains ALL of the super-class subscriptions.
|
||||||
subscriptions = subsAsList.toArray(new Subscription[0]);
|
subscriptions = subsAsList.toArray(EMPTY_SUBS);
|
||||||
localSuperSubs.put(messageClass, subscriptions);
|
localSuperSubs.put(messageClass, subscriptions);
|
||||||
|
|
||||||
subsSuperSingleREF.lazySet(this, localSuperSubs);
|
subsSuperSingleREF.lazySet(this, localSuperSubs);
|
||||||
@ -534,20 +498,28 @@ class SubscriptionManager {
|
|||||||
return subscriptions;
|
return subscriptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
/**
|
||||||
|
* @return can NOT return null
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
// save the subscriptions
|
// save the subscriptions
|
||||||
final Class<?>[] superClasses1 = this.classUtils.getSuperClasses(messageClass1); // never returns null, cached response
|
final Class<?>[] superClasses1 = this.classUtils.getSuperClasses(messageClass1); // never returns null, cached response
|
||||||
final Class<?>[] superClasses2 = this.classUtils.getSuperClasses(messageClass2); // never returns null, cached response
|
final Class<?>[] superClasses2 = this.classUtils.getSuperClasses(messageClass2); // never returns null, cached response
|
||||||
|
|
||||||
|
final MultiClass origMultiClass = classTree.get(messageClass1, messageClass2);
|
||||||
|
|
||||||
|
IdentityMap<MultiClass, Subscription[]> localSuperSubs = subsSuperMultiREF.get(this);
|
||||||
|
Subscription[] subscriptions = localSuperSubs.get(origMultiClass);
|
||||||
|
// the only time this is null, is when subscriptions DO NOT exist, and they haven't been calculated. Otherwise, if they are
|
||||||
|
// calculated and if they do not exist - this will be an empty array.
|
||||||
|
if (subscriptions == null) {
|
||||||
final IdentityMap<MultiClass, Subscription[]> localSubs = subsMultiREF.get(this);
|
final IdentityMap<MultiClass, Subscription[]> localSubs = subsMultiREF.get(this);
|
||||||
|
|
||||||
Class<?> superClass1;
|
Class<?> superClass1;
|
||||||
Class<?> superClass2;
|
Class<?> superClass2;
|
||||||
Subscription sub;
|
Subscription sub;
|
||||||
Subscription[] superSubs;
|
Subscription[] superSubs;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
|
|
||||||
final int length1 = superClasses1.length;
|
final int length1 = superClasses1.length;
|
||||||
@ -572,16 +544,18 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// never returns null
|
// never returns null
|
||||||
final MultiClass multiClass = classTree.get(superClass1,
|
MultiClass multiClass = classTree.get(superClass1,
|
||||||
superClass2);
|
superClass2);
|
||||||
|
|
||||||
superSubs = localSubs.get(multiClass);
|
superSubs = localSubs.get(multiClass);
|
||||||
|
|
||||||
|
//noinspection Duplicates
|
||||||
if (superSubs != null) {
|
if (superSubs != null) {
|
||||||
for (int k = 0; k < superSubs.length; k++) {
|
for (int k = 0; k < superSubs.length; k++) {
|
||||||
sub = superSubs[k];
|
sub = superSubs[k];
|
||||||
|
|
||||||
if (sub.getHandler().acceptsSubtypes()) {
|
if (sub.getHandler().acceptsSubtypes()) {
|
||||||
subsAsList.add(sub);
|
subsAsList.add(sub);
|
||||||
hasSubs = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,78 +563,100 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// subsAsList now contains ALL of the super-class subscriptions.
|
// subsAsList now contains ALL of the super-class subscriptions.
|
||||||
if (hasSubs) {
|
subscriptions = subsAsList.toArray(EMPTY_SUBS);
|
||||||
return subsAsList.toArray(new Subscription[0]);
|
localSuperSubs.put(origMultiClass, subscriptions);
|
||||||
}
|
|
||||||
else {
|
subsSuperMultiREF.lazySet(this, localSuperSubs);
|
||||||
// TODO: shortcut out if there are no handlers that accept subtypes
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return subscriptions;
|
||||||
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
public
|
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2) {
|
|
||||||
// ArrayList<Subscription> collection = getSubs(messageClass1, messageClass2); // can return null
|
|
||||||
//
|
|
||||||
// // now publish superClasses
|
|
||||||
// final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2,
|
|
||||||
// this); // NOT return null
|
|
||||||
//
|
|
||||||
// if (collection != null) {
|
|
||||||
// collection = new ArrayList<Subscription>(collection);
|
|
||||||
//
|
|
||||||
// if (!superSubs.isEmpty()) {
|
|
||||||
// collection.addAll(superSubs);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else if (!superSubs.isEmpty()) {
|
|
||||||
// collection = superSubs;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (collection != null) {
|
|
||||||
// return collection.toArray(new Subscription[0]);
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
return null;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
/**
|
||||||
public
|
* @return can NOT return null
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
*/
|
||||||
//
|
|
||||||
// ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3); // can return null
|
|
||||||
//
|
|
||||||
// // now publish superClasses
|
|
||||||
// final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2, messageClass3,
|
|
||||||
// this); // NOT return null
|
|
||||||
//
|
|
||||||
// if (collection != null) {
|
|
||||||
// collection = new ArrayList<Subscription>(collection);
|
|
||||||
//
|
|
||||||
// if (!superSubs.isEmpty()) {
|
|
||||||
// collection.addAll(superSubs);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else if (!superSubs.isEmpty()) {
|
|
||||||
// collection = superSubs;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (collection != null) {
|
|
||||||
// return collection.toArray(new Subscription[0]);
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
return null;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
public
|
||||||
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
return null;
|
// save the subscriptions
|
||||||
|
final Class<?>[] superClasses1 = this.classUtils.getSuperClasses(messageClass1); // never returns null, cached response
|
||||||
|
final Class<?>[] superClasses2 = this.classUtils.getSuperClasses(messageClass2); // never returns null, cached response
|
||||||
|
final Class<?>[] superClasses3 = this.classUtils.getSuperClasses(messageClass3); // never returns null, cached response
|
||||||
|
|
||||||
|
final MultiClass origMultiClass = classTree.get(messageClass1, messageClass2, messageClass3);
|
||||||
|
|
||||||
|
IdentityMap<MultiClass, Subscription[]> localSuperSubs = subsSuperMultiREF.get(this);
|
||||||
|
Subscription[] subscriptions = localSuperSubs.get(origMultiClass);
|
||||||
|
// the only time this is null, is when subscriptions DO NOT exist, and they haven't been calculated. Otherwise, if they are
|
||||||
|
// calculated and if they do not exist - this will be an empty array.
|
||||||
|
if (subscriptions == null) {
|
||||||
|
final IdentityMap<MultiClass, Subscription[]> localSubs = subsMultiREF.get(this);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Class<?> superClass1;
|
||||||
|
Class<?> superClass2;
|
||||||
|
Class<?> superClass3;
|
||||||
|
Subscription sub;
|
||||||
|
Subscription[] superSubs;
|
||||||
|
|
||||||
|
final int length1 = superClasses1.length;
|
||||||
|
final int length2 = superClasses2.length;
|
||||||
|
final int length3 = superClasses3.length;
|
||||||
|
|
||||||
|
ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length1 + length2);
|
||||||
|
|
||||||
|
for (int i = 0; i < length1; i++) {
|
||||||
|
superClass1 = superClasses1[i];
|
||||||
|
|
||||||
|
// only go over subtypes
|
||||||
|
if (superClass1 == messageClass1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < length2; j++) {
|
||||||
|
superClass2 = superClasses2[j];
|
||||||
|
|
||||||
|
// only go over subtypes
|
||||||
|
if (superClass2 == messageClass2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < length3; k++) {
|
||||||
|
superClass3 = superClasses3[j];
|
||||||
|
|
||||||
|
// only go over subtypes
|
||||||
|
if (superClass3 == messageClass3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// never returns null
|
||||||
|
MultiClass multiClass = classTree.get(superClass1,
|
||||||
|
superClass2,
|
||||||
|
superClass3);
|
||||||
|
|
||||||
|
superSubs = localSubs.get(multiClass);
|
||||||
|
|
||||||
|
//noinspection Duplicates
|
||||||
|
if (superSubs != null) {
|
||||||
|
for (int m = 0; m < superSubs.length; m++) {
|
||||||
|
sub = superSubs[m];
|
||||||
|
|
||||||
|
if (sub.getHandler().acceptsSubtypes()) {
|
||||||
|
subsAsList.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsAsList now contains ALL of the super-class subscriptions.
|
||||||
|
subscriptions = subsAsList.toArray(EMPTY_SUBS);
|
||||||
|
localSuperSubs.put(origMultiClass, subscriptions);
|
||||||
|
|
||||||
|
subsSuperMultiREF.lazySet(this, localSuperSubs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,68 +90,56 @@ class SubscriptionAsm extends Subscription {
|
|||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message) throws Throwable {
|
void publish(final Object message) throws Throwable {
|
||||||
final MethodAccess handler = this.handlerAccess;
|
final MethodAccess handler = this.handlerAccess;
|
||||||
final int handleIndex = this.methodIndex;
|
final int handleIndex = this.methodIndex;
|
||||||
final AsmInvocation invocation = this.invocation;
|
final AsmInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, handler, handleIndex, message);
|
invocation.invoke(listener, handler, handleIndex, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message1, final Object message2) throws Throwable {
|
void publish(final Object message1, final Object message2) throws Throwable {
|
||||||
final MethodAccess handler = this.handlerAccess;
|
final MethodAccess handler = this.handlerAccess;
|
||||||
final int handleIndex = this.methodIndex;
|
final int handleIndex = this.methodIndex;
|
||||||
final AsmInvocation invocation = this.invocation;
|
final AsmInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, handler, handleIndex, message1, message2);
|
invocation.invoke(listener, handler, handleIndex, message1, message2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
final MethodAccess handler = this.handlerAccess;
|
final MethodAccess handler = this.handlerAccess;
|
||||||
final int handleIndex = this.methodIndex;
|
final int handleIndex = this.methodIndex;
|
||||||
final AsmInvocation invocation = this.invocation;
|
final AsmInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,65 +83,53 @@ class SubscriptionReflection extends Subscription {
|
|||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message) throws Throwable {
|
void publish(final Object message) throws Throwable {
|
||||||
final Method method = this.method;
|
final Method method = this.method;
|
||||||
final ReflectionInvocation invocation = this.invocation;
|
final ReflectionInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, method, message);
|
invocation.invoke(listener, method, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message1, final Object message2) throws Throwable {
|
void publish(final Object message1, final Object message2) throws Throwable {
|
||||||
final Method method = this.method;
|
final Method method = this.method;
|
||||||
final ReflectionInvocation invocation = this.invocation;
|
final ReflectionInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, method, message1, message2);
|
invocation.invoke(listener, method, message1, message2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if messages were published
|
* @return true if messages were published
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
final Method method = this.method;
|
final Method method = this.method;
|
||||||
final ReflectionInvocation invocation = this.invocation;
|
final ReflectionInvocation invocation = this.invocation;
|
||||||
boolean hasSubs = false;
|
|
||||||
|
|
||||||
Entry current = headREF.get(this);
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
hasSubs = true;
|
|
||||||
listener = current.getValue();
|
listener = current.getValue();
|
||||||
current = current.next();
|
current = current.next();
|
||||||
|
|
||||||
invocation.invoke(listener, method, message1, message2, message3);
|
invocation.invoke(listener, method, message1, message2, message3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasSubs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import java.util.concurrent.ArrayBlockingQueue;
|
|||||||
*
|
*
|
||||||
* @author dorkbox, llc Date: 2/3/16
|
* @author dorkbox, llc Date: 2/3/16
|
||||||
*/
|
*/
|
||||||
public
|
public final
|
||||||
class AsyncABQ implements Synchrony {
|
class AsyncABQ implements Synchrony {
|
||||||
|
|
||||||
private final ArrayBlockingQueue<MessageHolder> dispatchQueue;
|
private final ArrayBlockingQueue<MessageHolder> dispatchQueue;
|
||||||
@ -147,8 +147,7 @@ class AsyncABQ implements Synchrony {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unfortunately, this isn't as friendly to GC as the disruptor is...
|
@Override
|
||||||
|
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
||||||
MessageHolder take = new MessageHolder();
|
MessageHolder take = new MessageHolder();
|
||||||
@ -162,13 +161,26 @@ class AsyncABQ implements Synchrony {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
||||||
this.dispatchQueue.put(new MessageHolder(subscriptions, message1, message2));
|
MessageHolder take = new MessageHolder();
|
||||||
|
take.type = MessageType.TWO;
|
||||||
|
take.subscriptions = subscriptions;
|
||||||
|
take.message1 = message1;
|
||||||
|
take.message2 = message2;
|
||||||
|
|
||||||
|
this.dispatchQueue.put(take);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
this.dispatchQueue.put(new MessageHolder(subscriptions, message1, message2, message3));
|
MessageHolder take = new MessageHolder();
|
||||||
|
take.type = MessageType.THREE;
|
||||||
|
take.subscriptions = subscriptions;
|
||||||
|
take.message1 = message1;
|
||||||
|
take.message2 = message2;
|
||||||
|
take.message3 = message3;
|
||||||
|
|
||||||
|
this.dispatchQueue.put(take);
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
|
@ -34,7 +34,7 @@ import java.util.concurrent.ArrayBlockingQueue;
|
|||||||
*
|
*
|
||||||
* @author dorkbox, llc Date: 2/3/16
|
* @author dorkbox, llc Date: 2/3/16
|
||||||
*/
|
*/
|
||||||
public
|
public final
|
||||||
class AsyncABQ_noGc implements Synchrony {
|
class AsyncABQ_noGc implements Synchrony {
|
||||||
|
|
||||||
private final ArrayBlockingQueue<MessageHolder> dispatchQueue;
|
private final ArrayBlockingQueue<MessageHolder> dispatchQueue;
|
||||||
@ -162,8 +162,7 @@ class AsyncABQ_noGc implements Synchrony {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unfortunately, this isn't as friendly to GC as the disruptor is...
|
@Override
|
||||||
|
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
||||||
MessageHolder take = gcQueue.take();
|
MessageHolder take = gcQueue.take();
|
||||||
@ -177,13 +176,26 @@ class AsyncABQ_noGc implements Synchrony {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
||||||
this.dispatchQueue.put(new MessageHolder(subscriptions, message1, message2));
|
MessageHolder take = gcQueue.take();
|
||||||
|
take.type = MessageType.TWO;
|
||||||
|
take.subscriptions = subscriptions;
|
||||||
|
take.message1 = message1;
|
||||||
|
take.message2 = message2;
|
||||||
|
|
||||||
|
this.dispatchQueue.put(take);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
this.dispatchQueue.put(new MessageHolder(subscriptions, message1, message2, message3));
|
MessageHolder take = gcQueue.take();
|
||||||
|
take.type = MessageType.THREE;
|
||||||
|
take.subscriptions = subscriptions;
|
||||||
|
take.message1 = message1;
|
||||||
|
take.message2 = message2;
|
||||||
|
take.message3 = message3;
|
||||||
|
|
||||||
|
this.dispatchQueue.put(take);
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
|
@ -40,7 +40,7 @@ import java.util.concurrent.locks.LockSupport;
|
|||||||
/**
|
/**
|
||||||
* @author dorkbox, llc Date: 2/3/16
|
* @author dorkbox, llc Date: 2/3/16
|
||||||
*/
|
*/
|
||||||
public
|
public final
|
||||||
class AsyncDisruptor implements Synchrony {
|
class AsyncDisruptor implements Synchrony {
|
||||||
|
|
||||||
private final ErrorHandlingSupport errorHandler;
|
private final ErrorHandlingSupport errorHandler;
|
||||||
@ -122,7 +122,7 @@ class AsyncDisruptor implements Synchrony {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
||||||
long seq = ringBuffer.next();
|
long seq = ringBuffer.next();
|
||||||
@ -138,13 +138,30 @@ class AsyncDisruptor implements Synchrony {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2) throws Throwable {
|
||||||
|
long seq = ringBuffer.next();
|
||||||
|
|
||||||
|
MessageHolder job = ringBuffer.get(seq);
|
||||||
|
job.type = MessageType.TWO;
|
||||||
|
job.subscriptions = subscriptions;
|
||||||
|
job.message1 = message1;
|
||||||
|
job.message2 = message2;
|
||||||
|
|
||||||
|
ringBuffer.publish(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
|
long seq = ringBuffer.next();
|
||||||
|
|
||||||
|
MessageHolder job = ringBuffer.get(seq);
|
||||||
|
job.type = MessageType.THREE;
|
||||||
|
job.subscriptions = subscriptions;
|
||||||
|
job.message1 = message1;
|
||||||
|
job.message3 = message2;
|
||||||
|
job.message2 = message3;
|
||||||
|
|
||||||
|
ringBuffer.publish(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the sequences used for processing work
|
// gets the sequences used for processing work
|
||||||
|
@ -20,12 +20,11 @@ import dorkbox.util.messagebus.subscription.Subscription;
|
|||||||
/**
|
/**
|
||||||
* @author dorkbox, llc Date: 2/2/15
|
* @author dorkbox, llc Date: 2/2/15
|
||||||
*/
|
*/
|
||||||
public
|
public final
|
||||||
class Sync implements Synchrony {
|
class Sync implements Synchrony {
|
||||||
public
|
public
|
||||||
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
void publish(final Subscription[] subscriptions, final Object message1) throws Throwable {
|
||||||
Subscription sub;
|
Subscription sub;
|
||||||
boolean hasSubs = false;
|
|
||||||
for (int i = 0; i < subscriptions.length; i++) {
|
for (int i = 0; i < subscriptions.length; i++) {
|
||||||
sub = subscriptions[i];
|
sub = subscriptions[i];
|
||||||
sub.publish(message1);
|
sub.publish(message1);
|
||||||
@ -55,13 +54,11 @@ class Sync implements Synchrony {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void start() {
|
void start() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,263 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.messagebus.utils;
|
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.util.IdentityMap;
|
|
||||||
import dorkbox.util.messagebus.common.ClassTree;
|
|
||||||
import dorkbox.util.messagebus.subscription.Subscription;
|
|
||||||
import dorkbox.util.messagebus.subscription.SubscriptionManager;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public final
|
|
||||||
class SubscriptionUtils {
|
|
||||||
private final ClassUtils superClass;
|
|
||||||
|
|
||||||
// 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 shutdown() on the original one
|
|
||||||
|
|
||||||
// keeps track of all subscriptions of the super classes of a message type.
|
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> superClassSubscriptions;
|
|
||||||
private final ClassTree<Class<?>> superClassSubscriptionsMulti;
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
SubscriptionUtils(final ClassUtils superClass, final float loadFactor, final int numberOfThreads) {
|
|
||||||
this.superClass = superClass;
|
|
||||||
|
|
||||||
|
|
||||||
// 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 IdentityMap<Class<?>, Subscription[]>(8, loadFactor);
|
|
||||||
this.superClassSubscriptionsMulti = new ClassTree<Class<?>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void clear() {
|
|
||||||
this.superClassSubscriptions.clear();
|
|
||||||
this.superClassSubscriptionsMulti.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// // ALWAYS register and create a cached version of the requested class + superClasses
|
|
||||||
// // ONLY called during subscribe
|
|
||||||
// public
|
|
||||||
// Subscription[] register(final Class<?> clazz, final SubscriptionManager subManager) {
|
|
||||||
// final IdentityMap<Class<?>, Subscription[]> local = this.superClassSubscriptions;
|
|
||||||
//
|
|
||||||
// // types was not empty, so collect subscriptions for each type and collate them
|
|
||||||
//
|
|
||||||
// // save the subscriptions
|
|
||||||
// final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz); // never returns null, cached response
|
|
||||||
//
|
|
||||||
// Class<?> superClass;
|
|
||||||
// Subscription[] superSubs;
|
|
||||||
// Subscription sub;
|
|
||||||
//
|
|
||||||
// final int length = superClasses.length;
|
|
||||||
// int superSubLength;
|
|
||||||
// final ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// superClass = superClasses[i];
|
|
||||||
// superSubs = subManager.getExactAsArray(superClass);
|
|
||||||
//
|
|
||||||
// if (superSubs != null) {
|
|
||||||
// superSubLength = superSubs.length;
|
|
||||||
// for (int j = 0; j < superSubLength; j++) {
|
|
||||||
// sub = superSubs[j];
|
|
||||||
//
|
|
||||||
// if (sub.getHandlerAccess().acceptsSubtypes()) {
|
|
||||||
// subsAsList.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final int size = subsAsList.size();
|
|
||||||
// if (size > 0) {
|
|
||||||
// Subscription[] subs = new Subscription[size];
|
|
||||||
// subsAsList.toArray(subs);
|
|
||||||
// local.put(clazz, subs);
|
|
||||||
//
|
|
||||||
// superClassSubscriptions = local;
|
|
||||||
//
|
|
||||||
// return subs;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array COPY of the super subscriptions for the specified type.
|
|
||||||
* <p/>
|
|
||||||
* This ALSO checks to see if the superClass accepts subtypes.
|
|
||||||
*
|
|
||||||
* @return CAN RETURN NULL
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
Subscription[] getSuperSubscriptions(final Class<?> clazz) {
|
|
||||||
// whenever our subscriptions change, this map is cleared.
|
|
||||||
return this.superClassSubscriptions.get(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array COPY of the super subscriptions for the specified type.
|
|
||||||
* <p/>
|
|
||||||
* This ALSO checks to see if the superClass accepts subtypes.
|
|
||||||
*
|
|
||||||
* @return CAN NOT RETURN NULL
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getSuperSubscriptions(final Class<?> clazz1, final Class<?> clazz2, final SubscriptionManager subManager) {
|
|
||||||
// // whenever our subscriptions change, this map is cleared.
|
|
||||||
// final MapTree<Class<?>, ArrayList<Subscription>> cached = this.superClassSubscriptionsMulti;
|
|
||||||
//
|
|
||||||
// ArrayList<Subscription> subs = cached.get(clazz1, clazz2);
|
|
||||||
//
|
|
||||||
// if (subs == null) {
|
|
||||||
// // types was not empty, so collect subscriptions for each type and collate them
|
|
||||||
//
|
|
||||||
// // save the subscriptions
|
|
||||||
// final Class<?>[] superClasses1 = this.superClass.getSuperClasses(clazz1); // never returns null, cached response
|
|
||||||
// final Class<?>[] superClasses2 = this.superClass.getSuperClasses(clazz2); // never returns null, cached response
|
|
||||||
//
|
|
||||||
// Class<?> superClass1;
|
|
||||||
// Class<?> superClass2;
|
|
||||||
// ArrayList<Subscription> superSubs;
|
|
||||||
// Subscription sub;
|
|
||||||
//
|
|
||||||
// final int length1 = superClasses1.length;
|
|
||||||
// final int length2 = superClasses2.length;
|
|
||||||
//
|
|
||||||
// subs = new ArrayList<Subscription>(length1 + length2);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < length1; i++) {
|
|
||||||
// superClass1 = superClasses1[i];
|
|
||||||
//
|
|
||||||
// // only go over subtypes
|
|
||||||
// if (superClass1 == clazz1) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int j = 0; j < length2; j++) {
|
|
||||||
// superClass2 = superClasses2[j];
|
|
||||||
//
|
|
||||||
// // only go over subtypes
|
|
||||||
// if (superClass2 == clazz2) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// superSubs = subManager.getExactAsArray(superClass1, superClass2);
|
|
||||||
// if (superSubs != null) {
|
|
||||||
// for (int k = 0; k < superSubs.size(); k++) {
|
|
||||||
// sub = superSubs.get(k);
|
|
||||||
//
|
|
||||||
// if (sub.getHandlerAccess().acceptsSubtypes()) {
|
|
||||||
// subs.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// subs.trimToSize();
|
|
||||||
// cached.put(subs, clazz1, clazz2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return subs;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array COPY of the super subscriptions for the specified type.
|
|
||||||
* <p/>
|
|
||||||
* This ALSO checks to see if the superClass accepts subtypes.
|
|
||||||
*
|
|
||||||
* @return CAN NOT RETURN NULL
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getSuperSubscriptions(final Class<?> clazz1, final Class<?> clazz2, final Class<?> clazz3,
|
|
||||||
final SubscriptionManager subManager) {
|
|
||||||
// // whenever our subscriptions change, this map is cleared.
|
|
||||||
// final MapTree<Class<?>, ArrayList<Subscription>> local = this.superClassSubscriptionsMulti;
|
|
||||||
//
|
|
||||||
// ArrayList<Subscription> subs = local.get(clazz1, clazz2, clazz3);
|
|
||||||
//
|
|
||||||
// if (subs == null) {
|
|
||||||
// // types was not empty, so collect subscriptions for each type and collate them
|
|
||||||
//
|
|
||||||
// // save the subscriptions
|
|
||||||
// final Class<?>[] superClasses1 = this.superClass.getSuperClasses(clazz1); // never returns null, cached response
|
|
||||||
// final Class<?>[] superClasses2 = this.superClass.getSuperClasses(clazz2); // never returns null, cached response
|
|
||||||
// final Class<?>[] superClasses3 = this.superClass.getSuperClasses(clazz3); // never returns null, cached response
|
|
||||||
//
|
|
||||||
// Class<?> superClass1;
|
|
||||||
// Class<?> superClass2;
|
|
||||||
// Class<?> superClass3;
|
|
||||||
// ArrayList<Subscription> superSubs;
|
|
||||||
// Subscription sub;
|
|
||||||
//
|
|
||||||
// final int length1 = superClasses1.length;
|
|
||||||
// final int length2 = superClasses2.length;
|
|
||||||
// final int length3 = superClasses3.length;
|
|
||||||
//
|
|
||||||
// subs = new ArrayList<Subscription>(length1 + length2 + length3);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < length1; i++) {
|
|
||||||
// superClass1 = superClasses1[i];
|
|
||||||
//
|
|
||||||
// // only go over subtypes
|
|
||||||
// if (superClass1 == clazz1) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int j = 0; j < length2; j++) {
|
|
||||||
// superClass2 = superClasses2[j];
|
|
||||||
//
|
|
||||||
// // only go over subtypes
|
|
||||||
// if (superClass2 == clazz2) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int k = 0; k < length3; k++) {
|
|
||||||
// superClass3 = superClasses3[j];
|
|
||||||
//
|
|
||||||
// // only go over subtypes
|
|
||||||
// if (superClass3 == clazz3) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// superSubs = subManager.getExactAsArray(superClass1, superClass2, superClass3);
|
|
||||||
// if (superSubs != null) {
|
|
||||||
// for (int m = 0; m < superSubs.size(); m++) {
|
|
||||||
// sub = superSubs.get(m);
|
|
||||||
//
|
|
||||||
// if (sub.getHandlerAccess().acceptsSubtypes()) {
|
|
||||||
// subs.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// subs.trimToSize();
|
|
||||||
// local.put(subs, clazz1, clazz2, clazz3);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return subs;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -220,7 +220,7 @@ public class SubscriptionManagerTest extends AssertSupport {
|
|||||||
ListenerFactory listeners = listeners(Overloading.ListenerBase.class, Overloading.ListenerSub.class);
|
ListenerFactory listeners = listeners(Overloading.ListenerBase.class, Overloading.ListenerSub.class);
|
||||||
|
|
||||||
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
||||||
final SubscriptionManager subscriptionManager = new SubscriptionManager(1, errorHandler);
|
final SubscriptionManager subscriptionManager = new SubscriptionManager(1);
|
||||||
|
|
||||||
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ public class SubscriptionManagerTest extends AssertSupport {
|
|||||||
|
|
||||||
private void runTestWith(final ListenerFactory listeners, final SubscriptionValidator validator) {
|
private void runTestWith(final ListenerFactory listeners, final SubscriptionValidator validator) {
|
||||||
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
||||||
final SubscriptionManager subscriptionManager = new SubscriptionManager(1, errorHandler);
|
final SubscriptionManager subscriptionManager = new SubscriptionManager(1);
|
||||||
|
|
||||||
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user