From 3674d6031b41a3c2d3110b78b61036ef2e4f4e12 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 20 Jan 2016 13:35:33 +0100 Subject: [PATCH] WIP single writer principle. have subsPerMessage + subsPerSuperMessage working --- src/dorkbox/util/messagebus/MessageBus.java | 8 +- .../PublisherExactWithSuperTypes.java | 21 +- .../subscription/SubscriptionManager.java | 238 ++++++++++++------ .../util/messagebus/utils/ClassUtils.java | 18 +- .../messagebus/utils/SubscriptionUtils.java | 110 ++++---- 5 files changed, 249 insertions(+), 146 deletions(-) diff --git a/src/dorkbox/util/messagebus/MessageBus.java b/src/dorkbox/util/messagebus/MessageBus.java index 7101801..f92fc61 100644 --- a/src/dorkbox/util/messagebus/MessageBus.java +++ b/src/dorkbox/util/messagebus/MessageBus.java @@ -133,8 +133,8 @@ class MessageBus implements IMessageBus { return; } - subscriptionManager.subscribe(listener); -// subscriptionWriter.subscribe(listener); +// subscriptionManager.subscribe(listener); + subscriptionWriter.subscribe(listener); } @Override @@ -144,8 +144,8 @@ class MessageBus implements IMessageBus { return; } - subscriptionManager.unsubscribe(listener); -// subscriptionWriter.unsubscribe(listener); +// subscriptionManager.unsubscribe(listener); + subscriptionWriter.unsubscribe(listener); } @Override diff --git a/src/dorkbox/util/messagebus/publication/PublisherExactWithSuperTypes.java b/src/dorkbox/util/messagebus/publication/PublisherExactWithSuperTypes.java index 178b336..e848dc9 100644 --- a/src/dorkbox/util/messagebus/publication/PublisherExactWithSuperTypes.java +++ b/src/dorkbox/util/messagebus/publication/PublisherExactWithSuperTypes.java @@ -40,18 +40,29 @@ class PublisherExactWithSuperTypes implements Publisher { void publish(final Synchrony synchrony, final Object message1) { try { final SubscriptionManager subManager = this.subManager; - final Class messageClass = message1.getClass(); + final Class message1Class = message1.getClass(); + boolean hasSubs = false; - final Subscription[] subscriptions = subManager.getExactAndSuper(messageClass); // can return null // Run subscriptions + final Subscription[] subscriptions = subManager.getExactAsArray(message1Class); // can return null if (subscriptions != null) { + hasSubs = true; synchrony.publish(subscriptions, message1); } - else { - // Dead Event must EXACTLY MATCH (no subclasses) - final Subscription[] deadSubscriptions = subManager.getExact(DeadMessage.class); // can return null + // Run superClasses + final Subscription[] superSubscriptions = subManager.getSuperExactAsArray(message1Class); // can return null + if (superSubscriptions != null) { + hasSubs = true; + synchrony.publish(superSubscriptions, message1); + } + + + // Run dead message subscriptions + if (!hasSubs) { + // Dead Event must EXACTLY MATCH (no subclasses) + final Subscription[] deadSubscriptions = subManager.getExactAsArray(DeadMessage.class); // can return null if (deadSubscriptions != null) { synchrony.publish(deadSubscriptions, new DeadMessage(message1)); } diff --git a/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java b/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java index 282fcc4..12f1676 100644 --- a/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java +++ b/src/dorkbox/util/messagebus/subscription/SubscriptionManager.java @@ -15,6 +15,7 @@ */ package dorkbox.util.messagebus.subscription; +import com.esotericsoftware.kryo.util.IdentityMap; import dorkbox.util.messagebus.common.HashMapTree; import dorkbox.util.messagebus.common.MessageHandler; import dorkbox.util.messagebus.error.ErrorHandlingSupport; @@ -25,9 +26,8 @@ import dorkbox.util.messagebus.utils.VarArgUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** * Permits subscriptions with a varying length of parameters as the signature, which must be match by the publisher for it to be accepted */ @@ -43,18 +43,29 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class SubscriptionManager { public static final float LOAD_FACTOR = 0.8F; + public static final Subscription[] SUBSCRIPTIONS = new Subscription[0]; - // TODO: during startup, precalculate the number of subscription listeners and x2 to save as subsPerListener expected max size + // TODO: during startup, pre-calculate the number of subscription listeners and x2 to save as subsPerListener expected max size // ONLY used by SUB/UNSUB // remember already processed classes that do not contain any message handlers - private final ConcurrentMap, Boolean> nonListeners; + private final IdentityMap, Boolean> nonListeners; + // ONLY used by SUB/UNSUB // all subscriptions per messageHandler type // this map provides fast access for subscribing and unsubscribing // once a collection of subscriptions is stored it does not change - private final ConcurrentMap, Subscription[]> subscriptionsPerListener; + private final IdentityMap, Subscription[]> subsPerListener; + + + // all subscriptions per message type. We perpetually KEEP the types + private volatile IdentityMap, Subscription[]> subsPerMessageSingle; + + // keeps track of all subscriptions of the super classes of a message type. + private volatile IdentityMap, Subscription[]> subsPerMessageSuperSingle; + + @@ -63,10 +74,6 @@ class SubscriptionManager { private final SubscriptionUtils subUtils; private final VarArgUtils varArgUtils; - // 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, 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) @@ -74,6 +81,18 @@ class SubscriptionManager { private final ClassUtils classUtils; + + // Recommended for best performance while adhering to the "single writer principle" + private final AtomicReferenceFieldUpdater subsPerMessageSingleREF = + AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class, + IdentityMap.class, + "subsPerMessageSingle"); + + private final AtomicReferenceFieldUpdater subsPerMessageSuperSingleRef = + AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class, + IdentityMap.class, + "subsPerMessageSuperSingle"); + //NOTE for multiArg, can use the memory address concatenated with other ones and then just put it in the 'single" map (convert single to // 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 // correct type too) @@ -85,10 +104,13 @@ class SubscriptionManager { classUtils = new ClassUtils(SubscriptionManager.LOAD_FACTOR); // modified ONLY during SUB/UNSUB - this.nonListeners = new ConcurrentHashMap, Boolean>(4, LOAD_FACTOR, numberOfThreads); + nonListeners = new IdentityMap, Boolean>(16, LOAD_FACTOR); + subsPerListener = new IdentityMap<>(32, LOAD_FACTOR); + subsPerMessageSingle = new IdentityMap, Subscription[]>(32, LOAD_FACTOR); + subsPerMessageSuperSingle = new IdentityMap, Subscription[]>(32, LOAD_FACTOR); + + - subscriptionsPerListener = new ConcurrentHashMap, Subscription[]>(32, LOAD_FACTOR, numberOfThreads); - subscriptionsPerMessageSingle = new ConcurrentHashMap, Subscription[]>(32, LOAD_FACTOR, numberOfThreads); this.subscriptionsPerMessageMulti = new HashMapTree, ArrayList>(); @@ -97,16 +119,19 @@ class SubscriptionManager { // var arg subscriptions keep track of which subscriptions can handle varArgs. SUB/UNSUB dumps it, so it is recreated dynamically. // it's a hit on SUB/UNSUB, but improves performance of handlers this.varArgUtils = new VarArgUtils(classUtils, LOAD_FACTOR, numberOfThreads); + + } public void shutdown() { this.nonListeners.clear(); - this.subscriptionsPerMessageSingle.clear(); + this.subsPerMessageSingle.clear(); + this.subsPerMessageSuperSingle.clear(); this.subscriptionsPerMessageMulti.clear(); - this.subscriptionsPerListener.clear(); + this.subsPerListener.clear(); this.classUtils.shutdown(); clear(); } @@ -154,13 +179,10 @@ class SubscriptionManager { // this is an array, because subscriptions for a specific listener CANNOT change, either they exist or do not exist. // ONCE subscriptions are in THIS map, they are considered AVAILABLE. - Subscription[] subscriptions = this.subscriptionsPerListener.get(listenerClass); + Subscription[] subscriptions = this.subsPerListener.get(listenerClass); // the subscriptions from the map were null, so create them if (subscriptions == null) { - // it is important to note that this section CAN be repeated. - // anything 'permanent' is saved. This is so the time spent inside the writelock is minimized. - final MessageHandler[] messageHandlers = MessageHandler.get(listenerClass); final int handlersSize = messageHandlers.length; @@ -170,7 +192,11 @@ class SubscriptionManager { return; } + // create the subscriptions + subscriptions = new Subscription[handlersSize]; + final IdentityMap, Subscription[]> subsPerMessageSingle = subsPerMessageSingleREF.get(this); +// final IdentityMap, Subscription[]> subsPerMessageSingle = this.subsPerMessageSingle; Subscription subscription; @@ -178,10 +204,6 @@ class SubscriptionManager { Class[] messageHandlerTypes; Class handlerType; - // create the subscriptions - final ConcurrentMap, Subscription[]> subsPerMessageSingle = this.subscriptionsPerMessageSingle; - subscriptions = new Subscription[handlersSize]; - for (int i = 0; i < handlersSize; i++) { // THE HANDLER IS THE SAME FOR ALL SUBSCRIPTIONS OF THE SAME TYPE! messageHandler = messageHandlers[i]; @@ -197,7 +219,7 @@ class SubscriptionManager { handlerType = messageHandlerTypes[0]; if (!subsPerMessageSingle.containsKey(handlerType)) { - subsPerMessageSingle.put(handlerType, new Subscription[0]); + subsPerMessageSingle.put(handlerType, SUBSCRIPTIONS); // this is copied to a larger array if necessary } @@ -208,50 +230,47 @@ class SubscriptionManager { // now subsPerMessageSingle has a unique list of subscriptions for a specific handlerType, and MAY already have subscriptions - final Subscription[] previousSubs = subscriptionsPerListener.putIfAbsent(listenerClass, subscriptions); // activates this sub for sub/unsub - if (previousSubs != null) { - // another thread beat us to creating subs (for this exact listenerClass). Since another thread won, we have to make sure - // all of the subscriptions are correct for a specific handler type, so we have to RECONSTRUCT the correct list again. - // This is to make sure that "invalid" subscriptions don't exist in subsPerMessageSingle. - // since nothing is yet "subscribed" we can assign the correct values for everything now - subscriptions = previousSubs; - } 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); + subsPerListener.put(listenerClass, subscriptions); // activates this sub for sub/unsub - subscription = subscriptions[i]; - subscription.subscribe(listener); // register this callback listener to this subscription + // we can now safely add for publication AND subscribe since the data structures are consistent + for (int i = 0; i < handlersSize; i++) { + subscription = subscriptions[i]; + subscription.subscribe(listener); // register this callback listener to this subscription - // THE HANDLER IS THE SAME FOR ALL SUBSCRIPTIONS OF THE SAME TYPE! - messageHandler = messageHandlers[i]; + // THE HANDLER IS THE SAME FOR ALL SUBSCRIPTIONS OF THE SAME TYPE! + messageHandler = messageHandlers[i]; - // register for publication - messageHandlerTypes = messageHandler.getHandledMessages(); - handlerType = messageHandlerTypes[0]; + // register for publication + messageHandlerTypes = messageHandler.getHandledMessages(); + handlerType = messageHandlerTypes[0]; - // makes this subscription visible for publication - 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); - } + // makes this subscription visible for publication + final Subscription[] currentSubs = subsPerMessageSingle.get(handlerType); + final int currentLength = currentSubs.length; - return; + // 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); + + // update the super types/varity types + registerSuperSubs(handlerType); } - } - // subscriptions already exist and must only be updated - Subscription subscription; - for (int i = 0; i < subscriptions.length; i++) { - subscription = subscriptions[i]; - subscription.subscribe(listener); + subsPerMessageSingleREF.lazySet(this, subsPerMessageSingle); +// SUBS_SINGLE.set(this, subsPerMessageSingle); +// this.subsPerMessageSingle = subsPerMessageSingle; + } + else { + // subscriptions already exist and must only be updated + Subscription subscription; + for (int i = 0; i < subscriptions.length; i++) { + subscription = subscriptions[i]; + subscription.subscribe(listener); + } } } @@ -267,7 +286,7 @@ class SubscriptionManager { // these are concurrent collections clear(); - final Subscription[] subscriptions = this.subscriptionsPerListener.get(listenerClass); + final Subscription[] subscriptions = this.subsPerListener.get(listenerClass); if (subscriptions != null) { Subscription subscription; @@ -278,6 +297,67 @@ class SubscriptionManager { } } + private + void registerSuperSubs(final Class clazz) { + final Class[] superClasses = this.classUtils.getSuperClasses(clazz); // never returns null, cached response + + final IdentityMap, Subscription[]> local = subsPerMessageSuperSingleRef.get(this); +// final IdentityMap, Subscription[]> local = this.subsPerMessageSuperSingle; + + // types was not empty, so collect subscriptions for each type and collate them + // save the subscriptions + + Class superClass; + Subscription[] superSubs; + Subscription sub; + + final int length = superClasses.length; + int superSubLength; + final ArrayList subsAsList = new ArrayList(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++) { + superClass = superClasses[i]; + superSubs = 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); + } + } + } + } + + final int size = subsAsList.size(); + if (size > 0) { + Subscription[] subs = new Subscription[size]; + subsAsList.toArray(subs); + local.put(clazz, subs); + + + subsPerMessageSuperSingleRef.lazySet(this, local); +// subsPerMessageSuperSingle = local; + } + } + + + + + + + + + + + + + + + public AtomicBoolean getVarArgPossibility() { @@ -291,7 +371,7 @@ class SubscriptionManager { public void clear() { - this.subUtils.clear(); +// this.subUtils.clear(); // this.varArgUtils.clear(); } @@ -369,12 +449,20 @@ class SubscriptionManager { } - public Subscription[] getExactAsArray(final Class messageClass) { - return subscriptionsPerMessageSingle.get(messageClass); + return (Subscription[]) subsPerMessageSingleREF.get(this).get(messageClass); +// return subsPerMessageSingle.get(messageClass); } + public + Subscription[] getSuperExactAsArray(final Class messageClass) { + // whenever our subscriptions change, this map is cleared. + return (Subscription[]) subsPerMessageSuperSingleRef.get(this).get(messageClass); +// return this.subsPerMessageSuperSingle.get(messageClass); + } + + public ArrayList getExactAsArray(final Class messageClass1, final Class messageClass2) { return subscriptionsPerMessageMulti.get(messageClass1, messageClass2); @@ -422,27 +510,31 @@ class SubscriptionManager { return null; } - // can NOT return null + // can return null public Subscription[] getExactAndSuper(final Class messageClass) { Subscription[] collection = getExactAsArray(messageClass); // can return null // now publish superClasses - final Subscription[] superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null + final Subscription[] superSubscriptions = getSuperExactAsArray(messageClass); // can return null if (collection != null) { - final int length = collection.length; - final int lengthSuper = superSubscriptions.length; + if (superSubscriptions != null) { + // but both into a single array + final int length = collection.length; + final int lengthSuper = superSubscriptions.length; - final Subscription[] newSubs = new Subscription[length + lengthSuper]; - System.arraycopy(collection, 0, newSubs, 0, length); - System.arraycopy(superSubscriptions, 0, newSubs, length, lengthSuper); + final Subscription[] newSubs = new Subscription[length + lengthSuper]; + System.arraycopy(collection, 0, newSubs, 0, length); + System.arraycopy(superSubscriptions, 0, newSubs, length, lengthSuper); - return newSubs; - } - else { - return superSubscriptions; + return newSubs; + } + + return collection; } + + return superSubscriptions; } // can return null diff --git a/src/dorkbox/util/messagebus/utils/ClassUtils.java b/src/dorkbox/util/messagebus/utils/ClassUtils.java index 2685bf4..b5707dc 100644 --- a/src/dorkbox/util/messagebus/utils/ClassUtils.java +++ b/src/dorkbox/util/messagebus/utils/ClassUtils.java @@ -15,27 +15,25 @@ */ package dorkbox.util.messagebus.utils; +import com.esotericsoftware.kryo.util.IdentityMap; + import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; public final class ClassUtils { - private final Map, Class> arrayCache; - private final Map, Class[]> superClassesCache; + private final IdentityMap, Class> arrayCache; + private final IdentityMap, Class[]> superClassesCache; /** * These data structures are never reset because the class hierarchy doesn't change at runtime */ public ClassUtils(final float loadFactor) { -// this.arrayCache = JavaVersionAdapter.concurrentMap(32, loadFactor, 1); -// this.superClassesCache = JavaVersionAdapter.concurrentMap(32, loadFactor, 1); - this.arrayCache = new IdentityHashMap, Class>(32); - this.superClassesCache = new IdentityHashMap, Class[]>(32); + this.arrayCache = new IdentityMap, Class>(32); + this.superClassesCache = new IdentityMap, Class[]>(32); } /** @@ -48,7 +46,7 @@ class ClassUtils { public Class[] getSuperClasses(final Class clazz) { // this is never reset, since it never needs to be. - final Map, Class[]> cache = this.superClassesCache; + final IdentityMap, Class[]> cache = this.superClassesCache; Class[] classes = cache.get(clazz); @@ -101,7 +99,7 @@ class ClassUtils { public Class getArrayClass(final Class c) { // this is never reset, since it never needs to be. - final Map, Class> cache = this.arrayCache; + final IdentityMap, Class> cache = this.arrayCache; Class clazz = cache.get(c); if (clazz == null) { diff --git a/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java b/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java index c85be54..330c3c8 100644 --- a/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java +++ b/src/dorkbox/util/messagebus/utils/SubscriptionUtils.java @@ -15,13 +15,12 @@ */ package dorkbox.util.messagebus.utils; +import com.esotericsoftware.kryo.util.IdentityMap; import dorkbox.util.messagebus.common.HashMapTree; import dorkbox.util.messagebus.subscription.Subscription; import dorkbox.util.messagebus.subscription.SubscriptionManager; import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; public final class SubscriptionUtils { @@ -30,7 +29,9 @@ 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, Subscription[]> superClassSubscriptions; + + // keeps track of all subscriptions of the super classes of a message type. + private volatile IdentityMap, Subscription[]> superClassSubscriptions; private final HashMapTree, ArrayList> superClassSubscriptionsMulti; @@ -41,7 +42,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, Subscription[]>(8, loadFactor, numberOfThreads); + this.superClassSubscriptions = new IdentityMap, Subscription[]>(8, loadFactor); this.superClassSubscriptionsMulti = new HashMapTree, ArrayList>(); } @@ -51,65 +52,66 @@ 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; - } +// // 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, 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); +// } +// } +// } +// } +// +// 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. *

* This ALSO checks to see if the superClass accepts subtypes. * - * @return CAN NOT RETURN NULL + * @return CAN RETURN NULL */ public - Subscription[] getSuperSubscriptions(final Class clazz, final SubscriptionManager subManager) { + Subscription[] getSuperSubscriptions(final Class clazz) { // whenever our subscriptions change, this map is cleared. - final Map, Subscription[]> local = this.superClassSubscriptions; - Subscription[] subs = local.get(clazz); - - if (subs != null) { - return subs; - } - - return register(clazz, subManager); + return this.superClassSubscriptions.get(clazz); } /**