[BREAKING] Use event class instead of event for timings. Fixes BUKKIT-4664

TimedRegisteredListener uses a reference to the first event fired. This
causes a memory leak in the server for any references that event has. This
changes TimedRegisteredListener to only store a reference to the class of
the event.

This change is intentionally a breaking change, as it is an obscure part
of the API. A non-breaking change would require the leak to be maintained
or an immediate update for any plugins using the method, as it would be an
indirect break.

A unit test is also included to check behavior of shared superclass
functionality.

By: Score_Under <seejay.11@gmail.com>
This commit is contained in:
Bukkit/Spigot
2012-07-05 23:51:12 +01:00
parent 9bc7b6277e
commit d8cfc3fa42
3 changed files with 86 additions and 14 deletions

View File

@@ -11,7 +11,7 @@ import org.bukkit.event.Listener;
public class TimedRegisteredListener extends RegisteredListener {
private int count;
private long totalTime;
private Event event;
private Class<? extends Event> eventClass;
private boolean multiple = false;
public TimedRegisteredListener(final Listener pluginListener, final EventExecutor eventExecutor, final EventPriority eventPriority, final Plugin registeredPlugin, final boolean listenCancelled) {
@@ -25,17 +25,25 @@ public class TimedRegisteredListener extends RegisteredListener {
return;
}
count++;
if (this.event == null) {
this.event = event;
}
else if (!this.event.getClass().equals(event.getClass())) {
Class<? extends Event> newEventClass = event.getClass();
if (this.eventClass == null) {
this.eventClass = newEventClass;
} else if (!this.eventClass.equals(newEventClass)) {
multiple = true;
this.eventClass = getCommonSuperclass(newEventClass, this.eventClass).asSubclass(Event.class);
}
long start = System.nanoTime();
super.callEvent(event);
totalTime += System.nanoTime() - start;
}
private static Class<?> getCommonSuperclass(Class<?> class1, Class<?> class2) {
while (!class1.isAssignableFrom(class2)) {
class1 = class1.getSuperclass();
}
return class1;
}
/**
* Resets the call count and total time for this listener
*/
@@ -63,18 +71,26 @@ public class TimedRegisteredListener extends RegisteredListener {
}
/**
* Gets the first event this listener handled
* Gets the class of the events this listener handled. If it handled
* multiple classes of event, the closest shared superclass will be
* returned, such that for any event this listener has handled,
* <code>this.getEventClass().isAssignableFrom(event.getClass())</code>
* and no class
* <code>this.getEventClass().isAssignableFrom(clazz)
* && this.getEventClass() != clazz &&
* event.getClass().isAssignableFrom(clazz)</code> for all handled events.
*
* @return An event handled by this RegisteredListener
* @return the event class handled by this RegisteredListener
*/
public Event getEvent() {
return event;
public Class<? extends Event> getEventClass() {
return eventClass;
}
/**
* Gets whether this listener has handled multiple events
* Gets whether this listener has handled multiple events, such that for
* some two events, <code>eventA.getClass() != eventB.getClass()</code>.
*
* @return True if this listener has handled multiple events
* @return true if this listener has handled multiple events
*/
public boolean hasMultiple() {
return multiple;