Waterfall/BungeeCord-Patches/0016-Use-ASM-for-events-via...

273 lines
10 KiB
Diff

From 81c24fbb9f86c8bd14cc33208504f53147235cff Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Sun, 28 Feb 2016 22:46:22 -0700
Subject: [PATCH] Use ASM for events, via the Event4J library
According to benchmarks, this is about 7 nanoseconds faster than reflection (34 ns -> 27 ns).
Event4J falls back to Java 8's new MethodHandle if the event method isn't public.
diff --git a/event/pom.xml b/event/pom.xml
index b6ef990..ff72600 100644
--- a/event/pom.xml
+++ b/event/pom.xml
@@ -17,4 +17,24 @@
<name>Waterfall-Event</name>
<description>Generic java event dispatching API intended for use with Waterfall.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>net.techcable</groupId>
+ <artifactId>event4j</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-all</artifactId>
+ <version>5.0.4</version>
+ </dependency>
+ </dependencies>
+
+ <repositories>
+ <repository>
+ <id>techcable-repo</id>
+ <url>https://repo.techcable.net/content/groups/public/</url>
+ </repository>
+ </repositories>
</project>
diff --git a/event/src/main/java/net/md_5/bungee/event/EventBus.java b/event/src/main/java/net/md_5/bungee/event/EventBus.java
index 5b5d420..c93aa16 100644
--- a/event/src/main/java/net/md_5/bungee/event/EventBus.java
+++ b/event/src/main/java/net/md_5/bungee/event/EventBus.java
@@ -1,26 +1,13 @@
package net.md_5.bungee.event;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.logging.Level;
import java.util.logging.Logger;
public class EventBus
{
- private final Map<Class<?>, Map<Byte, Map<Object, Method[]>>> byListenerAndPriority = new HashMap<>();
- private final Map<Class<?>, EventHandlerMethod[]> byEventBaked = new ConcurrentHashMap<>();
- private final Lock lock = new ReentrantLock();
+ private final net.techcable.event4j.EventBus<Object, Object> event4JBus = net.techcable.event4j.EventBus.builder()
+ .eventMarker((m) -> m.isAnnotationPresent(EventHandler.class) ? m.getAnnotation(EventHandler.class)::priority : null)
+ .build();
private final Logger logger;
public EventBus()
@@ -35,167 +22,16 @@ public class EventBus
public void post(Object event)
{
- EventHandlerMethod[] handlers = byEventBaked.get( event.getClass() );
-
- if ( handlers != null )
- {
- for ( EventHandlerMethod method : handlers )
- {
- try
- {
- method.invoke( event );
- } catch ( IllegalAccessException ex )
- {
- throw new Error( "Method became inaccessible: " + event, ex );
- } catch ( IllegalArgumentException ex )
- {
- throw new Error( "Method rejected target/argument: " + event, ex );
- } catch ( InvocationTargetException ex )
- {
- logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
- }
- }
- }
- }
-
- private Map<Class<?>, Map<Byte, Set<Method>>> findHandlers(Object listener)
- {
- Map<Class<?>, Map<Byte, Set<Method>>> handler = new HashMap<>();
- for ( Method m : listener.getClass().getDeclaredMethods() )
- {
- EventHandler annotation = m.getAnnotation( EventHandler.class );
- if ( annotation != null )
- {
- Class<?>[] params = m.getParameterTypes();
- if ( params.length != 1 )
- {
- logger.log( Level.INFO, "Method {0} in class {1} annotated with {2} does not have single argument", new Object[]
- {
- m, listener.getClass(), annotation
- } );
- continue;
- }
- Map<Byte, Set<Method>> prioritiesMap = handler.get( params[0] );
- if ( prioritiesMap == null )
- {
- prioritiesMap = new HashMap<>();
- handler.put( params[0], prioritiesMap );
- }
- Set<Method> priority = prioritiesMap.get( annotation.priority() );
- if ( priority == null )
- {
- priority = new HashSet<>();
- prioritiesMap.put( annotation.priority(), priority );
- }
- priority.add( m );
- }
- }
- return handler;
+ event4JBus.fire(event);
}
public void register(Object listener)
{
- Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
- lock.lock();
- try
- {
- for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
- {
- Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
- if ( prioritiesMap == null )
- {
- prioritiesMap = new HashMap<>();
- byListenerAndPriority.put( e.getKey(), prioritiesMap );
- }
- for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() )
- {
- Map<Object, Method[]> currentPriorityMap = prioritiesMap.get( entry.getKey() );
- if ( currentPriorityMap == null )
- {
- currentPriorityMap = new HashMap<>();
- prioritiesMap.put( entry.getKey(), currentPriorityMap );
- }
- Method[] baked = new Method[ entry.getValue().size() ];
- currentPriorityMap.put( listener, entry.getValue().toArray( baked ) );
- }
- bakeHandlers( e.getKey() );
- }
- } finally
- {
- lock.unlock();
- }
+ event4JBus.register(listener);
}
public void unregister(Object listener)
{
- Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
- lock.lock();
- try
- {
- for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
- {
- Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
- if ( prioritiesMap != null )
- {
- for ( Byte priority : e.getValue().keySet() )
- {
- Map<Object, Method[]> currentPriority = prioritiesMap.get( priority );
- if ( currentPriority != null )
- {
- currentPriority.remove( listener );
- if ( currentPriority.isEmpty() )
- {
- prioritiesMap.remove( priority );
- }
- }
- }
- if ( prioritiesMap.isEmpty() )
- {
- byListenerAndPriority.remove( e.getKey() );
- }
- }
- bakeHandlers( e.getKey() );
- }
- } finally
- {
- lock.unlock();
- }
- }
-
- /**
- * Shouldn't be called without first locking the writeLock; intended for use
- * only inside {@link #register(java.lang.Object) register(Object)} or
- * {@link #unregister(java.lang.Object) unregister(Object)}.
- */
- private void bakeHandlers(Class<?> eventClass)
- {
- Map<Byte, Map<Object, Method[]>> handlersByPriority = byListenerAndPriority.get( eventClass );
- if ( handlersByPriority != null )
- {
- List<EventHandlerMethod> handlersList = new ArrayList<>( handlersByPriority.size() * 2 );
-
- // Either I'm really tired, or the only way we can iterate between Byte.MIN_VALUE and Byte.MAX_VALUE inclusively,
- // with only a byte on the stack is by using a do {} while() format loop.
- byte value = Byte.MIN_VALUE;
- do
- {
- Map<Object, Method[]> handlersByListener = handlersByPriority.get( value );
- if ( handlersByListener != null )
- {
- for ( Map.Entry<Object, Method[]> listenerHandlers : handlersByListener.entrySet() )
- {
- for ( Method method : listenerHandlers.getValue() )
- {
- EventHandlerMethod ehm = new EventHandlerMethod( listenerHandlers.getKey(), method );
- handlersList.add( ehm );
- }
- }
- }
- } while ( value++ < Byte.MAX_VALUE );
- byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) );
- } else
- {
- byEventBaked.remove( eventClass );
- }
+ event4JBus.unregister(listener);
}
}
diff --git a/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java b/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java
deleted file mode 100644
index ad19c02..0000000
--- a/event/src/main/java/net/md_5/bungee/event/EventHandlerMethod.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.md_5.bungee.event;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-@AllArgsConstructor
-public class EventHandlerMethod
-{
-
- @Getter
- private final Object listener;
- @Getter
- private final Method method;
-
- public void invoke(Object event) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
- {
- method.invoke( listener, event );
- }
-}
--
2.8.3