diff --git a/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java b/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java index edeac46..282fcc4 100644 --- a/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java +++ b/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** * Permits subscriptions with a varying length of parameters as the signature, which must be match by the publisher for it to be accepted @@ -67,7 +66,7 @@ class SubscriptionManager { // 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 // write access is synchronized and happens only when a listener of a specific class is registered the first time - final ConcurrentMap, List> subscriptionsPerMessageSingle; + final ConcurrentMap, Subscription[]> subscriptionsPerMessageSingle; private final HashMapTree, ArrayList> subscriptionsPerMessageMulti; // shortcut publication if we know there is no possibility of varArg (ie: a method that has an array as arguments) @@ -89,7 +88,7 @@ class SubscriptionManager { this.nonListeners = new ConcurrentHashMap, Boolean>(4, LOAD_FACTOR, numberOfThreads); subscriptionsPerListener = new ConcurrentHashMap, Subscription[]>(32, LOAD_FACTOR, numberOfThreads); - subscriptionsPerMessageSingle = new ConcurrentHashMap, List>(32, LOAD_FACTOR, numberOfThreads); + subscriptionsPerMessageSingle = new ConcurrentHashMap, Subscription[]>(32, LOAD_FACTOR, numberOfThreads); this.subscriptionsPerMessageMulti = new HashMapTree, ArrayList>(); @@ -180,7 +179,7 @@ class SubscriptionManager { Class handlerType; // create the subscriptions - final ConcurrentMap, List> subsPerMessageSingle = this.subscriptionsPerMessageSingle; + final ConcurrentMap, Subscription[]> subsPerMessageSingle = this.subscriptionsPerMessageSingle; subscriptions = new Subscription[handlersSize]; for (int i = 0; i < handlersSize; i++) { @@ -198,7 +197,7 @@ class SubscriptionManager { handlerType = messageHandlerTypes[0]; if (!subsPerMessageSingle.containsKey(handlerType)) { - subsPerMessageSingle.put(handlerType, new CopyOnWriteArrayList()); + subsPerMessageSingle.put(handlerType, new Subscription[0]); } @@ -220,6 +219,9 @@ class SubscriptionManager { } else { // we can now safely add for publication AND subscribe since the data structures are consistent for (int i = 0; i < handlersSize; i++) { + // register the super types/varity types + subUtils.register(listenerClass, this); + subscription = subscriptions[i]; subscription.subscribe(listener); // register this callback listener to this subscription @@ -231,7 +233,14 @@ class SubscriptionManager { handlerType = messageHandlerTypes[0]; // makes this subscription visible for publication - subsPerMessageSingle.get(handlerType).add(subscription); + final Subscription[] currentSubs = subsPerMessageSingle.get(handlerType); + final int currentLength = currentSubs.length; + + // add the new subscription to the beginning of the array + final Subscription[] newSubs = new Subscription[currentLength + 1]; + newSubs[0] = subscription; + System.arraycopy(currentSubs, 0, newSubs, 1, currentLength); + subsPerMessageSingle.put(handlerType, newSubs); } return; @@ -362,7 +371,7 @@ class SubscriptionManager { public - List getExactAsArray(final Class messageClass) { + Subscription[] getExactAsArray(final Class messageClass) { return subscriptionsPerMessageSingle.get(messageClass); } @@ -379,17 +388,7 @@ class SubscriptionManager { // can return null public Subscription[] getExact(final Class messageClass) { - final List collection = getExactAsArray(messageClass); - - if (collection != null) { - // convert to Array because the subscriptions can change and we want safe iteration over the list - final Subscription[] subscriptions = new Subscription[collection.size()]; - collection.toArray(subscriptions); - - return subscriptions; - } - - return null; + return getExactAsArray(messageClass); } // can return null @@ -423,32 +422,26 @@ class SubscriptionManager { return null; } - // can return null + // can NOT return null public Subscription[] getExactAndSuper(final Class messageClass) { - List collection = getExactAsArray(messageClass); // can return null + Subscription[] collection = getExactAsArray(messageClass); // can return null // now publish superClasses - final ArrayList superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null + final Subscription[] superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null if (collection != null) { - collection = new ArrayList(collection); + final int length = collection.length; + final int lengthSuper = superSubscriptions.length; - if (!superSubscriptions.isEmpty()) { - collection.addAll(superSubscriptions); - } - } - else if (!superSubscriptions.isEmpty()) { - collection = superSubscriptions; - } + final Subscription[] newSubs = new Subscription[length + lengthSuper]; + System.arraycopy(collection, 0, newSubs, 0, length); + System.arraycopy(superSubscriptions, 0, newSubs, length, lengthSuper); - if (collection != null) { - final Subscription[] subscriptions = new Subscription[collection.size()]; - collection.toArray(subscriptions); - return subscriptions; + return newSubs; } else { - return null; + return superSubscriptions; } } diff --git a/src/dorkbox/util/messagebus/utils/ClassUtils.java b/src/dorkbox/util/messagebus/utils/ClassUtils.java index ed15d03..2685bf4 100644 --- a/src/dorkbox/util/messagebus/utils/ClassUtils.java +++ b/src/dorkbox/util/messagebus/utils/ClassUtils.java @@ -95,15 +95,18 @@ class ClassUtils { * race conditions will result in duplicate answers, which we don't care if happens * never returns null * never resets + * + * https://bugs.openjdk.java.net/browse/JDK-6525802 (fixed this in 2007, so Array.newInstance is just as fast (via intrinsics) new []) */ public Class getArrayClass(final Class c) { + // this is never reset, since it never needs to be. final Map, Class> cache = this.arrayCache; Class clazz = cache.get(c); if (clazz == null) { // messy, but the ONLY way to do it. Array super types are also arrays - final Object[] newInstance = (Object[]) Array.newInstance(c, 1); + final Object[] newInstance = (Object[]) Array.newInstance(c, 0); clazz = newInstance.getClass(); cache.put(c, clazz); } diff --git a/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java b/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java index ea043d4..c85be54 100644 --- a/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java +++ b/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java @@ -20,7 +20,6 @@ import dorkbox.util.messagebus.subscription.Subscription; import dorkbox.util.messagebus.subscription.SubscriptionManager; import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -31,7 +30,7 @@ class SubscriptionUtils { // 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 - private final Map, ArrayList> superClassSubscriptions; + private final Map, Subscription[]> superClassSubscriptions; private final HashMapTree, ArrayList> superClassSubscriptionsMulti; @@ -42,7 +41,7 @@ class SubscriptionUtils { // 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 ConcurrentHashMap, ArrayList>(8, loadFactor, numberOfThreads); + this.superClassSubscriptions = new ConcurrentHashMap, Subscription[]>(8, loadFactor, numberOfThreads); this.superClassSubscriptionsMulti = new HashMapTree, ArrayList>(); } @@ -52,6 +51,46 @@ class SubscriptionUtils { this.superClassSubscriptionsMulti.clear(); } + // ALWAYS register and create a cached version of the requested class + superClasses + public + Subscription[] register(final Class clazz, final SubscriptionManager subManager) { + final Map, 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 subsAsList = new ArrayList(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.getHandler().acceptsSubtypes()) { + subsAsList.add(sub); + } + } + } + } + + Subscription[] subs = new Subscription[subsAsList.size()]; + subsAsList.toArray(subs); + local.put(clazz, subs); + + return subs; + } /** * Returns an array COPY of the super subscriptions for the specified type. @@ -61,47 +100,16 @@ class SubscriptionUtils { * @return CAN NOT RETURN NULL */ public - ArrayList getSuperSubscriptions(final Class clazz, final SubscriptionManager subManager) { + Subscription[] getSuperSubscriptions(final Class clazz, final SubscriptionManager subManager) { // whenever our subscriptions change, this map is cleared. - final Map, ArrayList> local = this.superClassSubscriptions; + final Map, Subscription[]> local = this.superClassSubscriptions; + Subscription[] subs = local.get(clazz); - ArrayList subs = local.get(clazz); - - if (subs == null) { - // 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; - List superSubs; - Subscription sub; - - final int length = superClasses.length; - int superSubLength; - subs = new ArrayList(length); - - for (int i = 0; i < length; i++) { - superClass = superClasses[i]; - superSubs = subManager.getExactAsArray(superClass); - - if (superSubs != null) { - superSubLength = superSubs.size(); - for (int j = 0; j < superSubLength; j++) { - sub = superSubs.get(j); - - if (sub.getHandler().acceptsSubtypes()) { - subs.add(sub); - } - } - } - } - - subs.trimToSize(); - local.put(clazz, subs); + if (subs != null) { + return subs; } - return subs; + return register(clazz, subManager); } /** diff --git a/src/dorkbox/util/messagebus/utils/VarArgUtils.java b/src/dorkbox/util/messagebus/utils/VarArgUtils.java index cebdf20..d9bf282 100644 --- a/src/dorkbox/util/messagebus/utils/VarArgUtils.java +++ b/src/dorkbox/util/messagebus/utils/VarArgUtils.java @@ -21,16 +21,15 @@ import dorkbox.util.messagebus.subscription.Subscription; import dorkbox.util.messagebus.subscription.SubscriptionManager; import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public final class VarArgUtils { - private final Map, ArrayList> varArgSubscriptionsSingle; + private final Map, Subscription[]> varArgSubscriptionsSingle; private final HashMapTree, ArrayList> varArgSubscriptionsMulti; - private final Map, ArrayList> varArgSuperSubscriptionsSingle; + private final Map, Subscription[]> varArgSuperSubscriptionsSingle; private final HashMapTree, ArrayList> varArgSuperSubscriptionsMulti; private final ClassUtils superClassUtils; @@ -41,10 +40,10 @@ class VarArgUtils { this.superClassUtils = superClassUtils; - this.varArgSubscriptionsSingle = new ConcurrentHashMap, ArrayList>(16, loadFactor, numberOfThreads); + this.varArgSubscriptionsSingle = new ConcurrentHashMap, Subscription[]>(16, loadFactor, numberOfThreads); this.varArgSubscriptionsMulti = new HashMapTree, ArrayList>(); - this.varArgSuperSubscriptionsSingle = new ConcurrentHashMap, ArrayList>(16, loadFactor, numberOfThreads); + this.varArgSuperSubscriptionsSingle = new ConcurrentHashMap, Subscription[]>(16, loadFactor, numberOfThreads); this.varArgSuperSubscriptionsMulti = new HashMapTree, ArrayList>(); } @@ -65,38 +64,39 @@ class VarArgUtils { public Subscription[] getVarArgSubscriptions(final Class messageClass, final SubscriptionManager subManager) { // whenever our subscriptions change, this map is cleared. - final Map, ArrayList> local = this.varArgSubscriptionsSingle; + final Map, Subscription[]> local = this.varArgSubscriptionsSingle; - ArrayList varArgSubs = local.get(messageClass); + Subscription[] varArgSubs = local.get(messageClass); if (varArgSubs == null) { // this gets (and caches) our array type. This is never cleared. final Class arrayVersion = this.superClassUtils.getArrayClass(messageClass); - final List subs = subManager.getExactAsArray(arrayVersion); + final Subscription[] subs = subManager.getExactAsArray(arrayVersion); if (subs != null) { - final int length = subs.size(); - varArgSubs = new ArrayList(length); + final int length = subs.length; + final ArrayList varArgSubsAsList = new ArrayList(length); Subscription sub; for (int i = 0; i < length; i++) { - sub = subs.get(i); + sub = subs[i]; if (sub.getHandler().acceptsVarArgs()) { - varArgSubs.add(sub); + varArgSubsAsList.add(sub); } } + varArgSubs = new Subscription[varArgSubsAsList.size()]; + varArgSubsAsList.toArray(varArgSubs); + local.put(messageClass, varArgSubs); } else { - varArgSubs = new ArrayList(0); + varArgSubs = new Subscription[0]; } } - final Subscription[] subscriptions = new Subscription[varArgSubs.size()]; - varArgSubs.toArray(subscriptions); - return subscriptions; + return varArgSubs; } @@ -106,20 +106,10 @@ class VarArgUtils { // and then, returns the array'd version superclass subscriptions public Subscription[] getVarArgSuperSubscriptions(final Class messageClass, final SubscriptionManager subManager) { - final ArrayList subs = getVarArgSuperSubscriptions_List(messageClass, subManager); - - final Subscription[] returnedSubscriptions = new Subscription[subs.size()]; - subs.toArray(returnedSubscriptions); - return returnedSubscriptions; - } - - // CAN NOT RETURN NULL - private - ArrayList getVarArgSuperSubscriptions_List(final Class messageClass, final SubscriptionManager subManager) { // whenever our subscriptions change, this map is cleared. - final Map, ArrayList> local = this.varArgSuperSubscriptionsSingle; + final Map, Subscription[]> local = this.varArgSuperSubscriptionsSingle; - ArrayList varArgSuperSubs = local.get(messageClass); + Subscription[] varArgSuperSubs = local.get(messageClass); if (varArgSuperSubs == null) { // this gets (and caches) our array type. This is never cleared. @@ -127,7 +117,7 @@ class VarArgUtils { final Class[] types = this.superClassUtils.getSuperClasses(arrayVersion); final int typesLength = types.length; - varArgSuperSubs = new ArrayList(typesLength); + varArgSuperSubs = new Subscription[typesLength]; if (typesLength == 0) { local.put(messageClass, varArgSuperSubs); @@ -135,9 +125,10 @@ class VarArgUtils { } + // there are varArgs super classes for this messageClass Class type; Subscription sub; - List subs; + Subscription[] subs; int length; MessageHandler handlerMetadata; @@ -146,20 +137,23 @@ class VarArgUtils { subs = subManager.getExactAsArray(type); if (subs != null) { - length = subs.size(); - varArgSuperSubs = new ArrayList(length); + length = subs.length; + final ArrayList varArgSuperSubsAsList = new ArrayList(length); for (int j = 0; j < length; j++) { - sub = subs.get(j); + sub = subs[j]; handlerMetadata = sub.getHandler(); if (handlerMetadata.acceptsSubtypes() && handlerMetadata.acceptsVarArgs()) { - varArgSuperSubs.add(sub); + varArgSuperSubsAsList.add(sub); } } + + varArgSuperSubs = new Subscription[varArgSuperSubsAsList.size()]; + varArgSuperSubsAsList.toArray(varArgSuperSubs); } else { - varArgSuperSubs = new ArrayList(0); + varArgSuperSubs = new Subscription[0]; } } @@ -180,17 +174,17 @@ class VarArgUtils { ArrayList subs = local.get(messageClass1, messageClass2); - if (subs == null) { - // the message class types are not the same, so look for a common superClass varArg subscription. - // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages - final ArrayList varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager); - final ArrayList varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager); - - subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2); - - subs.trimToSize(); - local.put(subs, messageClass1, messageClass2); - } +// if (subs == null) { +// // the message class types are not the same, so look for a common superClass varArg subscription. +// // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages +// final ArrayList varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager); +// final ArrayList varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager); +// +// subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2); +// +// subs.trimToSize(); +// local.put(subs, messageClass1, messageClass2); +// } final Subscription[] returnedSubscriptions = new Subscription[subs.size()]; subs.toArray(returnedSubscriptions); @@ -209,19 +203,19 @@ class VarArgUtils { ArrayList subs = local.get(messageClass1, messageClass2, messageClass3); - if (subs == null) { - // the message class types are not the same, so look for a common superClass varArg subscription. - // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages - final ArrayList varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager); - final ArrayList varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager); - final ArrayList varargSuperSubscriptions3 = getVarArgSuperSubscriptions_List(messageClass3, subManager); - - subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2); - subs = ClassUtils.findCommon(subs, varargSuperSubscriptions3); - - subs.trimToSize(); - local.put(subs, messageClass1, messageClass2, messageClass3); - } +// if (subs == null) { +// // the message class types are not the same, so look for a common superClass varArg subscription. +// // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages +// final ArrayList varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager); +// final ArrayList varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager); +// final ArrayList varargSuperSubscriptions3 = getVarArgSuperSubscriptions_List(messageClass3, subManager); +// +// subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2); +// subs = ClassUtils.findCommon(subs, varargSuperSubscriptions3); +// +// subs.trimToSize(); +// local.put(subs, messageClass1, messageClass2, messageClass3); +// } final Subscription[] returnedSubscriptions = new Subscription[subs.size()]; subs.toArray(returnedSubscriptions);