Handler Caching #37

Merged
kske merged 5 commits from f/handler-caching into develop 2022-01-18 17:11:38 +01:00
1 changed files with 54 additions and 11 deletions
Showing only changes of commit 5468bddb35 - Show all commits

View File

@ -90,6 +90,16 @@ public final class EventBus {
*/
private final Map<Class<?>, TreeSet<EventHandler>> bindings = new ConcurrentHashMap<>();
/**
* A cache mapping an event class to all handlers the event should be dispatched to. This
* includes polymorphic handlers that don't reference the event class explicitly. If an event
* class is not contained inside this cache, the {@link #bindings} have to be traversed manually
* in search of applicable handlers.
*
* @since 1.3.0
*/
private final Map<Class<?>, TreeSet<EventHandler>> bindingCache = new ConcurrentHashMap<>();
/**
* Stores all registered event listeners (which declare event handlers) and prevents them from
* being garbage collected.
@ -175,24 +185,32 @@ public final class EventBus {
* Searches for the event handlers bound to an event class. This includes polymorphic handlers
* that are bound to a supertype of the event class.
*
* @implNote If the given event type was requested in the past, the handlers are retrieved from
* the {@link #bindingCache}. If not, the entire {@link #bindings} are traversed in
* search of polymorphic handlers compatible with the event type.
* @param eventType the event type to use for the search
* @return a navigable set containing the applicable handlers in descending order of priority
* @since 1.2.0
*/
private NavigableSet<EventHandler> getHandlersFor(Class<?> eventType) {
if (bindingCache.containsKey(eventType)) {
return bindingCache.get(eventType);
} else {
// Get handlers defined for the event class
TreeSet<EventHandler> handlers =
bindings.getOrDefault(eventType, new TreeSet<>(byPriority));
// Get handlers defined for the event class
TreeSet<EventHandler> handlers =
bindings.getOrDefault(eventType, new TreeSet<>(byPriority));
// Get polymorphic handlers
for (var binding : bindings.entrySet())
if (binding.getKey().isAssignableFrom(eventType))
for (var handler : binding.getValue())
if (handler.isPolymorphic())
handlers.add(handler);
// Get polymorphic handlers
for (var binding : bindings.entrySet())
if (binding.getKey().isAssignableFrom(eventType))
for (var handler : binding.getValue())
if (handler.isPolymorphic())
handlers.add(handler);
return handlers;
bindingCache.put(eventType, handlers);
return handlers;
}
}
/**
@ -369,15 +387,28 @@ public final class EventBus {
}
/**
* Inserts a new handler into the {@link #bindings} map.
* Inserts a new handler into the {@link #bindings} map. Additionally, the handler is placed
* inside the {@link #bindingCache} where applicable.
*
* @param handler the handler to bind
* @since 1.2.0
*/
private void bindHandler(EventHandler handler) {
// Bind handler
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>(byPriority));
logger.log(Level.DEBUG, "Binding event handler {0}", handler);
bindings.get(handler.getEventType()).add(handler);
// Insert handler into cache
bindingCache.putIfAbsent(handler.getEventType(), new TreeSet<>(byPriority));
bindingCache.get(handler.getEventType()).add(handler);
// Handler is polymorphic => insert where applicable
if (handler.isPolymorphic())
for (var binding : bindingCache.entrySet())
if (binding.getKey().isAssignableFrom(handler.getEventType()))
binding.getValue().add(handler);
}
/**
@ -402,6 +433,18 @@ public final class EventBus {
}
}
// Remove bindings from cache
for (var binding : bindingCache.values()) {
var it = binding.iterator();
kske marked this conversation as resolved Outdated
Outdated
Review

Maybe add a comment on why == instead of equals.

Maybe add a comment on why `==` instead of `equals`.
Outdated
Review

As we implicitly test using equals in registerListener(...), I will change this as well.

As we implicitly test using `equals` in `registerListener(...)`, I will change this as well.
while (it.hasNext()) {
var handler = it.next();
if (handler.getListener() == listener) {
logger.log(Level.TRACE, "Removing event handler {0} from cache", handler);
it.remove();
}
}
}
// Remove the listener itself
registeredListeners.remove(listener);
}