Implement more efficient handles.

This commit is contained in:
asofold 2016-06-16 23:34:52 +02:00
parent 2bf3e14ab9
commit d6bb1e84bc
2 changed files with 114 additions and 21 deletions

View File

@ -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<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
/** Listeners for registry events. */
private final Map<Class<?>, Collection<IGenericInstanceRegistryListener<?>>> listeners = new HashMap<Class<?>, Collection<IGenericInstanceRegistryListener<?>>>();
/** Owned handles by the class they have been registered for. */
private final Map<Class<?>, IGenericInstanceHandle<?>> uniqueHandles = new LinkedHashMap<Class<?>, IGenericInstanceHandle<?>>();
/** Handles created within this class, that have to be detached. */
private final Set<IGenericInstanceHandle<?>> ownedHandles = new LinkedHashSet<IGenericInstanceHandle<?>>();
@ -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 <T> IGenericInstanceHandle<T> getGenericInstanceHandle(Class<T> 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<T> handle = new GenericInstanceHandle<T>(registeredFor, this, this);
ownedHandles.add(handle);
Collection<IGenericInstanceRegistryListener<?>> registered = listeners.get(registeredFor);
if (registered == null) {
registered = new HashSet<IGenericInstanceRegistryListener<?>>();
listeners.put(registeredFor, registered);
@SuppressWarnings("unchecked")
ReferenceCountHandle<T> handle = (ReferenceCountHandle<T>) uniqueHandles.get(registeredFor);
if (handle == null) {
handle = new ReferenceCountHandle<T>(registeredFor, this, this);
ownedHandles.add(handle);
uniqueHandles.put(registeredFor, handle);
Collection<IGenericInstanceRegistryListener<?>> registered = listeners.get(registeredFor);
if (registered == null) {
registered = new HashSet<IGenericInstanceRegistryListener<?>>();
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() {

View File

@ -13,7 +13,82 @@ import fr.neatmonster.nocheatplus.components.registry.GenericInstanceRegistry;
public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListener<T>, IGenericInstanceHandle<T> {
// TODO: <? extends T> ?
// 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 <T>
*/
public static class ParentDelegateHandle<T> implements IGenericInstanceHandle<T> {
private final IGenericInstanceHandle<T> parent;
private boolean disabled = false;
public ParentDelegateHandle(Class<T> registeredFor, GenericInstanceRegistry registry,
IGenericInstanceHandle<T> 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 <T>
*/
public static class ReferenceCountHandle<T> extends GenericInstanceHandle<T> {
private int references = 0;
public ReferenceCountHandle(Class<T> 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<T> getNewHandle() {
references ++;
return new ParentDelegateHandle<T>(getRegisteredFor(), getRegistry(), this);
}
}
private GenericInstanceRegistry registry;
private IUnregisterGenericInstanceListener unregister;
@ -22,8 +97,6 @@ public class GenericInstanceHandle<T> 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<T> implements IGenericInstanceRegistryListene
handle = null;
registeredFor = null;
registry = null;
unregister.unregisterGenericInstanceListener(registeredFor, this);
if (unregister != null) {
unregister.unregisterGenericInstanceListener(registeredFor, this);
}
unregister = null;
}
}
public Class<T> getRegisteredFor() {
return registeredFor;
}
public GenericInstanceRegistry getRegistry() {
return registry;
}
public IUnregisterGenericInstanceListener getUnregister() {
return unregister;
}
}