From d6bb1e84bc578a3383e22b3be67d81ea7deb3cfe Mon Sep 17 00:00:00 2001 From: asofold Date: Thu, 16 Jun 2016 23:34:52 +0200 Subject: [PATCH] Implement more efficient handles. --- .../DefaultGenericInstanceRegistry.java | 40 ++++---- .../registry/event/GenericInstanceHandle.java | 95 ++++++++++++++++++- 2 files changed, 114 insertions(+), 21 deletions(-) diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java index 19342d5a..c360c298 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/DefaultGenericInstanceRegistry.java @@ -4,11 +4,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import fr.neatmonster.nocheatplus.components.registry.event.GenericInstanceHandle; +import fr.neatmonster.nocheatplus.components.registry.event.GenericInstanceHandle.ReferenceCountHandle; import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle; import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceRegistryListener; import fr.neatmonster.nocheatplus.components.registry.event.IUnregisterGenericInstanceListener; @@ -17,12 +18,17 @@ import fr.neatmonster.nocheatplus.logging.details.ILogString; public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, IUnregisterGenericInstanceListener { + // TODO: Test cases. + /** Storage for generic instances registration. */ private final Map, Object> instances = new HashMap, Object>(); /** Listeners for registry events. */ private final Map, Collection>> listeners = new HashMap, Collection>>(); + /** Owned handles by the class they have been registered for. */ + private final Map, IGenericInstanceHandle> uniqueHandles = new LinkedHashMap, IGenericInstanceHandle>(); + /** Handles created within this class, that have to be detached. */ private final Set> ownedHandles = new LinkedHashSet>(); @@ -47,8 +53,9 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, listeners.remove(registeredFor); } } - if ((listener instanceof IGenericInstanceHandle) && ownedHandles.contains(listener)) { + if ((listener instanceof ReferenceCountHandle) && ownedHandles.contains(listener)) { ownedHandles.remove(listener); + uniqueHandles.remove(registeredFor); ((IGenericInstanceHandle) listener).disableHandle(); } } @@ -114,22 +121,21 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, @Override public IGenericInstanceHandle getGenericInstanceHandle(Class registeredFor) { - /* - * More efficient should be to return a wrapper for a unique instance, - * for which disableHandle runs once, so only one listener per - * registered class is necessary, which then uses reference counting for - * actual removal. That's double-wrapped then (returned instance disable - * once -> reference counting instance -> actual instance). - */ - final IGenericInstanceHandle handle = new GenericInstanceHandle(registeredFor, this, this); - ownedHandles.add(handle); - Collection> registered = listeners.get(registeredFor); - if (registered == null) { - registered = new HashSet>(); - listeners.put(registeredFor, registered); + @SuppressWarnings("unchecked") + ReferenceCountHandle handle = (ReferenceCountHandle) uniqueHandles.get(registeredFor); + if (handle == null) { + handle = new ReferenceCountHandle(registeredFor, this, this); + ownedHandles.add(handle); + uniqueHandles.put(registeredFor, handle); + Collection> registered = listeners.get(registeredFor); + if (registered == null) { + registered = new HashSet>(); + listeners.put(registeredFor, registered); + } + registered.add((IGenericInstanceRegistryListener) handle); } - registered.add((IGenericInstanceRegistryListener) handle); - return handle; + // else: no need to register. + return handle.getNewHandle(); } public void clear() { diff --git a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java index 66b0a919..cbd3858d 100644 --- a/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java +++ b/NCPPlugin/src/main/java/fr/neatmonster/nocheatplus/components/registry/event/GenericInstanceHandle.java @@ -13,7 +13,82 @@ import fr.neatmonster.nocheatplus.components.registry.GenericInstanceRegistry; public class GenericInstanceHandle implements IGenericInstanceRegistryListener, IGenericInstanceHandle { // TODO: ? - // TODO: Might move to NCPPlugin, or later split (mostly) interface based api from default implementations. + + /** + * Delegates getHandle, disables the parent only once (meant for reference + * counting). + * + * @author asofold + * + * @param + */ + public static class ParentDelegateHandle implements IGenericInstanceHandle { + + private final IGenericInstanceHandle parent; + private boolean disabled = false; + + public ParentDelegateHandle(Class registeredFor, GenericInstanceRegistry registry, + IGenericInstanceHandle parent) { + this.parent = parent; + } + + @Override + public T getHandle() { + if (disabled) { + throw new RuntimeException("Already disabled."); + } + else { + return parent.getHandle(); + } + } + + @Override + public void disableHandle() { + if (!disabled) { + disabled = true; + parent.disableHandle(); + } + } + + } + + /** + * Allow fetching PrarentDelegate instances, increasing reference count with + * each returned one. Really unregister only with reaching a count of zero + * on disableHandle. This way only one instance needs to be updated. + * + * @author asofold + * + * @param + */ + public static class ReferenceCountHandle extends GenericInstanceHandle { + + private int references = 0; + + public ReferenceCountHandle(Class registeredFor, GenericInstanceRegistry registry, + IUnregisterGenericInstanceListener unregister) { + super(registeredFor, registry, unregister); + } + + @Override + public void disableHandle() { + references --; + // Only really unregister once. + if (references == 0) { + super.disableHandle(); + } + } + + /** + * Retrieve a new instance referencing this one. + * @return + */ + public IGenericInstanceHandle getNewHandle() { + references ++; + return new ParentDelegateHandle(getRegisteredFor(), getRegistry(), this); + } + + } private GenericInstanceRegistry registry; private IUnregisterGenericInstanceListener unregister; @@ -22,8 +97,6 @@ public class GenericInstanceHandle implements IGenericInstanceRegistryListene private boolean initialized = false; private boolean disabled = false; - // TODO: Remove method? - /** * Note that this doesn't register with the registry, as the registry may * return unique handles on request rather. @@ -81,9 +154,23 @@ public class GenericInstanceHandle implements IGenericInstanceRegistryListene handle = null; registeredFor = null; registry = null; - unregister.unregisterGenericInstanceListener(registeredFor, this); + if (unregister != null) { + unregister.unregisterGenericInstanceListener(registeredFor, this); + } unregister = null; } } + public Class getRegisteredFor() { + return registeredFor; + } + + public GenericInstanceRegistry getRegistry() { + return registry; + } + + public IUnregisterGenericInstanceListener getUnregister() { + return unregister; + } + }