mirror of
https://github.com/PaperMC/Waterfall.git
synced 2024-11-08 19:50:51 +01:00
80bed7979d
Its apparently the cause of many issues we are currently having
273 lines
10 KiB
Diff
273 lines
10 KiB
Diff
From 86c5823b0bfdfc21e38850b46bb486c4e10e940c 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
|
|
|