From 836e2639929af5e20fde2a54fd969d65157cd8ef Mon Sep 17 00:00:00 2001 From: RealMangoRage <64402114+RealMangorage@users.noreply.github.com> Date: Wed, 22 May 2024 12:56:47 -0700 Subject: [PATCH] Minor Optimizations for MangoBot --- .../mangorage/mboteventbus/base/Event.java | 2 +- .../mangorage/mboteventbus/base/EventBus.java | 94 ++++++------------- .../mboteventbus/base/ListenerKey.java | 33 ------- .../mboteventbus/base/ListenerListImpl.java | 48 +++------- .../mboteventbus/impl/IEventBus.java | 2 +- .../mboteventbus/impl/IListenerList.java | 6 +- .../mangorage/mangobot/test/EventBusTest.java | 40 +++++--- 7 files changed, 71 insertions(+), 154 deletions(-) delete mode 100644 src/main/java/org/mangorage/mboteventbus/base/ListenerKey.java diff --git a/src/main/java/org/mangorage/mboteventbus/base/Event.java b/src/main/java/org/mangorage/mboteventbus/base/Event.java index 47a627c..2be07cc 100644 --- a/src/main/java/org/mangorage/mboteventbus/base/Event.java +++ b/src/main/java/org/mangorage/mboteventbus/base/Event.java @@ -22,5 +22,5 @@ package org.mangorage.mboteventbus.base; -public abstract class Event { +public class Event { } diff --git a/src/main/java/org/mangorage/mboteventbus/base/EventBus.java b/src/main/java/org/mangorage/mboteventbus/base/EventBus.java index 350786a..4672762 100644 --- a/src/main/java/org/mangorage/mboteventbus/base/EventBus.java +++ b/src/main/java/org/mangorage/mboteventbus/base/EventBus.java @@ -25,9 +25,10 @@ import org.mangorage.mboteventbus.impl.IEventBus; import org.mangorage.mboteventbus.impl.IListenerList; -import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Semaphore; import java.util.function.Consumer; public final class EventBus implements IEventBus { @@ -39,47 +40,52 @@ private record ListenerKey(Class eventClass, Class genericClass) { } private final Map> LISTENERS = new ConcurrentHashMap<>(); + private final Semaphore writeLock = new Semaphore(1, true); - - @SuppressWarnings("unchecked") public void addListener(int priority, Class eventClass, Consumer eventConsumer) { - IListenerList list = (IListenerList) getListenerList(eventClass, null); + IListenerList list = getListenerList(eventClass, null); if (list == null) return; - list.register(eventConsumer, "default", priority); + list.register(eventConsumer, "default -> " + eventClass, priority); } - - @SuppressWarnings("unchecked") public , G> void addGenericListener(int priority, Class genericClassFilter, Class eventType, Consumer genericEventListener) { - IListenerList list = (IListenerList) getListenerList(eventType, genericClassFilter); + IListenerList list = getListenerList(eventType, genericClassFilter); if (list == null) return; - list.register(genericEventListener, "default", priority); + list.register(genericEventListener, "default -> " + eventType + " -> " + genericClassFilter, priority); } - public void post(Event event) { + public void post(T event) { Class genericType = null; if (event instanceof GenericEvent genericEvent) genericType = genericEvent.getGenericType(); - var listeners = getListenerList(event.getClass(), genericType); - if (listeners != null) - listeners.accept(event); + writeLock.acquireUninterruptibly(); + IListenerList listeners = getListenerList(getEventClass(event), genericType); + writeLock.release(); + if (listeners != null) listeners.accept(event); } - private IListenerList getListenerList(Class eventClass, Class genericClass) { + @SuppressWarnings("unchecked") + private Class getEventClass(T event) { + return (Class) event.getClass(); + } + + @SuppressWarnings("unchecked") + private IListenerList getListenerList(Class eventClass, Class genericClass) { + var list = getListenerListInternal(eventClass, genericClass); + return list == null ? null : (IListenerList) list; + } + + @SuppressWarnings("unchecked") + private IListenerList getListenerListInternal(Class eventClass, Class genericClass) { var key = new ListenerKey(eventClass, genericClass); var list = LISTENERS.get(key); - if (list != null) { - return list; - } + if (list != null) return list; - if (eventClass == Object.class) + if (eventClass == Object.class || eventClass == Event.class && genericClass != null) return null; - if (eventClass == Event.class && genericClass != null) - return null; - - return LISTENERS.computeIfAbsent(key, e -> new ListenerListImpl<>(new ArrayList<>(), getListenerList(eventClass.getSuperclass(), genericClass))); + return LISTENERS.computeIfAbsent(key, e -> new ListenerListImpl<>(new CopyOnWriteArrayList<>(), (IListenerList) getListenerListInternal(eventClass.getSuperclass(), genericClass))); } @Override @@ -91,48 +97,4 @@ public void startup() { public void shutdown() { } - - - public static class MyEvent extends Event { - public MyEvent() { - //super(Integer.class); - } - } - - public static class MyBetterEvent extends MyEvent { - } - - public static class MyCrazyEvent extends MyBetterEvent { - } - - - public static void main(String[] args) { - IEventBus bus = create(); - - -// bus.addGenericListener(10, Integer.class, MyEvent.class, e -> { -// System.out.println("Sweet -> " + e.getClass()); -// }); -// - bus.addGenericListener(10, Integer.class, GenericEvent.class, e -> { - System.out.println("Generic -> " + e.getClass()); - - }); - - bus.addListener(10, Event.class, e -> { - System.out.println(e.getClass()); - }); - - bus.addListener(10, MyEvent.class, e -> { - System.out.println("Sweeet! -> " + e.getClass()); - }); - - bus.post(new MyBetterEvent()); - bus.post(new MyEvent()); - bus.post(new MyCrazyEvent()); - bus.post(new GenericEvent<>(Integer.class) { - }); - - System.out.println("END"); - } } diff --git a/src/main/java/org/mangorage/mboteventbus/base/ListenerKey.java b/src/main/java/org/mangorage/mboteventbus/base/ListenerKey.java deleted file mode 100644 index a97e30c..0000000 --- a/src/main/java/org/mangorage/mboteventbus/base/ListenerKey.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2024. MangoRage - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE - * OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.mangorage.mboteventbus.base; - -public record ListenerKey(Class eventClass, Class genericClass) { - public static ListenerKey of(Class eventClass) { - return new ListenerKey(eventClass, null); - } - - public static ListenerKey ofGeneric(Class eventClass, Class genericClass) { - return new ListenerKey(eventClass, genericClass); - } -} diff --git a/src/main/java/org/mangorage/mboteventbus/base/ListenerListImpl.java b/src/main/java/org/mangorage/mboteventbus/base/ListenerListImpl.java index b1334a4..dc83a06 100644 --- a/src/main/java/org/mangorage/mboteventbus/base/ListenerListImpl.java +++ b/src/main/java/org/mangorage/mboteventbus/base/ListenerListImpl.java @@ -25,80 +25,56 @@ import org.mangorage.basicutils.misc.Lazy; import org.mangorage.mboteventbus.impl.IListenerList; -import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Consumer; import java.util.stream.Stream; +@SuppressWarnings("unchecked") public class ListenerListImpl implements IListenerList { private final List> backingList; private final IListenerList parent; - private final Set> children = new HashSet<>(); + private final Set> children = new CopyOnWriteArraySet<>(); private final Lazy[]> listeners; - public ListenerListImpl(List> backingList, IListenerList parent) { + public ListenerListImpl(List> backingList, IListenerList parent) { this.backingList = backingList; this.parent = parent; if (parent != null) parent.addChild(this); - this.listeners = Lazy.concurrentOf(() -> { - if (parent != null) { - return Stream.of(backingList, List.of(parent.getListeners())) - .flatMap(List::stream) - .sorted() - .toArray(Listener[]::new); - } - - return backingList.toArray(Listener[]::new); - }); + this.listeners = Lazy.concurrentOf(() -> parent == null ? backingList.toArray(Listener[]::new) : Stream.of(backingList, List.of(parent.getListeners())) + .flatMap(List::stream) + .sorted() + .toArray(Listener[]::new)); } - /** - * @param child - */ @Override - public void addChild(IListenerList child) { + public void addChild(IListenerList child) { children.add(child); } - /** - * @param event - */ @Override - public void accept(Event event) { + public void accept(T event) { for (Listener listener : getListeners()) { - listener.consumer().accept((T) event); + listener.consumer().accept(event); } } - /** - * @param eventConsumer - * @param name - * @param priority - */ @Override public void register(Consumer eventConsumer, String name, int priority) { backingList.add(new Listener<>(priority, name, eventConsumer)); - if (parent != null) parent.invalidate(); children.forEach(IListenerList::invalidate); + if (parent != null) parent.invalidate(); } - /** - * @return - */ @Override public Listener[] getListeners() { return listeners.get(); } - /** - * - */ @Override public void invalidate() { listeners.invalidate(); } - - } diff --git a/src/main/java/org/mangorage/mboteventbus/impl/IEventBus.java b/src/main/java/org/mangorage/mboteventbus/impl/IEventBus.java index 3aef946..f705b64 100644 --- a/src/main/java/org/mangorage/mboteventbus/impl/IEventBus.java +++ b/src/main/java/org/mangorage/mboteventbus/impl/IEventBus.java @@ -32,7 +32,7 @@ public interface IEventBus { , G> void addGenericListener(int priority, Class genericClass, Class genericEvent, Consumer genericEventListener); - void post(Event event); + void post(T event); void startup(); void shutdown(); diff --git a/src/main/java/org/mangorage/mboteventbus/impl/IListenerList.java b/src/main/java/org/mangorage/mboteventbus/impl/IListenerList.java index 58bf917..7015bb3 100644 --- a/src/main/java/org/mangorage/mboteventbus/impl/IListenerList.java +++ b/src/main/java/org/mangorage/mboteventbus/impl/IListenerList.java @@ -22,18 +22,16 @@ package org.mangorage.mboteventbus.impl; -import org.mangorage.mboteventbus.base.Event; import org.mangorage.mboteventbus.base.Listener; import java.util.function.Consumer; public interface IListenerList { - void accept(Event event); + void accept(T event); void register(Consumer eventConsumer, String name, int priority); Listener[] getListeners(); - void invalidate(); - void addChild(IListenerList child); + void addChild(IListenerList child); } diff --git a/src/test/java/org/mangorage/mangobot/test/EventBusTest.java b/src/test/java/org/mangorage/mangobot/test/EventBusTest.java index 7a9faaa..60000c0 100644 --- a/src/test/java/org/mangorage/mangobot/test/EventBusTest.java +++ b/src/test/java/org/mangorage/mangobot/test/EventBusTest.java @@ -26,37 +26,51 @@ import org.mangorage.mboteventbus.base.Event; import org.mangorage.mboteventbus.base.EventBus; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + public class EventBusTest { public static class MyEvent extends Event { } + public static class MyOther extends MyEvent { + } + + + public static final ExecutorService ex = Executors.newWorkStealingPool(); - public static void main(String[] args) { + + public static void main(String[] args) throws InterruptedException { var bus = EventBus.create(); var registration = TimedTest.of(() -> { - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 1000; i++) { bus.addListener(10, Event.class, e -> { }); - bus.addListener(10, MyEvent.class, e -> { + bus.addListener(10, MyOther.class, e -> { }); } }); + long amount = 100000; var post = TimedTest.of(() -> { - bus.post(new MyEvent()); + for (int i = 0; i < amount; i++) { + bus.post(new MyOther()); + bus.post(new Event()); + } }); + List myEvents = new ArrayList<>(); + for (int i = 0; i < amount; i++) + myEvents.add(new MyEvent()); + + myEvents.stream().parallel().forEach(bus::post); + System.out.println("Registration time for 1 listener -> " + registration.getResult()); - long total = 0; - long amount = 10000; - - for (int i = 0; i < amount; i++) { - var result = post.getResult(); - System.out.println("Post time for Post #%s -> ".formatted(i) + result); - total += result; - } - System.out.println("AVG -> " + total / amount); + System.out.println("AVG -> " + post.getResult() / amount); + var a = 1; } }