Tagging interface for event handlers

Support events in the TorchAPI
Static registry, non-static registration methods.
Support unregister by assembly.
This commit is contained in:
Westin Miller
2017-10-09 00:42:21 -07:00
parent 716e6cbc04
commit 013dc43c2f
8 changed files with 130 additions and 45 deletions

View File

@@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Managers.EventManager namespace Torch.API.Managers.Event
{ {
/// <summary> /// <summary>
/// Attribute indicating that a method should be invoked when the event occurs. /// Attribute indicating that a method should be invoked when the event occurs.

View File

@@ -1,10 +1,4 @@
using System; namespace Torch.API.Managers.Event
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Managers.EventManager
{ {
public interface IEvent public interface IEvent
{ {

View File

@@ -0,0 +1,9 @@
namespace Torch.API.Managers.Event
{
/// <summary>
/// Interface used to tag an event handler. This does <b>not</b> register it with the event manager.
/// </summary>
public interface IEventHandler
{
}
}

View File

@@ -0,0 +1,27 @@
using System.Runtime.CompilerServices;
namespace Torch.API.Managers.Event
{
/// <summary>
/// Manager class responsible for registration of event handlers.
/// </summary>
public interface IEventManager
{
/// <summary>
/// Registers all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to register</param>
/// <returns><b>true</b> if added, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
bool RegisterHandler(IEventHandler handler);
/// <summary>
/// Unregisters all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to unregister</param>
/// <returns><b>true</b> if removed, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
bool UnregisterHandler(IEventHandler handler);
}
}

View File

@@ -1,13 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using Torch.API.Managers.Event;
using Torch.Managers.EventManager;
namespace Torch.Managers.EventManager namespace Torch.Managers.Event
{ {
/// <summary> /// <summary>
/// Represents an ordered list of callbacks. /// Represents an ordered list of callbacks.
@@ -49,7 +48,7 @@ namespace Torch.Managers.EventManager
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <inheritdoc/> /// <inheritdoc/>
public void AddHandler(MethodInfo method, object instance) public void AddHandler(MethodInfo method, IEventHandler instance)
{ {
try try
{ {
@@ -64,7 +63,7 @@ namespace Torch.Managers.EventManager
} }
/// <inheritdoc/> /// <inheritdoc/>
public int RemoveHandlers(object instance) public int RemoveHandlers(IEventHandler instance)
{ {
try try
{ {

View File

@@ -3,23 +3,21 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using NLog; using NLog;
using Torch.API; using Torch.API;
using Torch.API.Managers; using Torch.API.Managers.Event;
using VRage.Game.ModAPI; using Torch.Managers.EventManager;
namespace Torch.Managers.EventManager namespace Torch.Managers.Event
{ {
/// <summary> /// <summary>
/// Manager class responsible for managing registration and dispatching of events. /// Manager class responsible for managing registration and dispatching of events.
/// </summary> /// </summary>
public class EventManager : Manager public class EventManager : Manager, IEventManager
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private static Dictionary<Type, IEventList> _eventLists = new Dictionary<Type, IEventList>(); private static readonly Dictionary<Type, IEventList> _eventLists = new Dictionary<Type, IEventList>();
static EventManager() static EventManager()
{ {
@@ -41,11 +39,7 @@ namespace Torch.Managers.EventManager
} }
} }
/// <summary> /// <inheritdoc/>
/// Finds all event handlers in the given type, and its base types
/// </summary>
/// <param name="exploreType">Type to explore</param>
/// <returns>All event handlers</returns>
private static IEnumerable<MethodInfo> EventHandlers(Type exploreType) private static IEnumerable<MethodInfo> EventHandlers(Type exploreType)
{ {
IEnumerable<MethodInfo> enumerable = exploreType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) IEnumerable<MethodInfo> enumerable = exploreType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
@@ -62,11 +56,8 @@ namespace Torch.Managers.EventManager
return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable; return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable;
} }
/// <summary> /// <inheritdoc/>
/// Registers all handlers the given instance owns. private static void RegisterHandlerInternal(IEventHandler instance)
/// </summary>
/// <param name="instance">Instance to register handlers from</param>
private static void RegisterHandler(object instance)
{ {
foreach (MethodInfo handler in EventHandlers(instance.GetType())) foreach (MethodInfo handler in EventHandlers(instance.GetType()))
{ {
@@ -96,15 +87,86 @@ namespace Torch.Managers.EventManager
/// Unregisters all handlers owned by the given instance /// Unregisters all handlers owned by the given instance
/// </summary> /// </summary>
/// <param name="instance">Instance</param> /// <param name="instance">Instance</param>
private static void UnregisterHandlers(object instance) private static void UnregisterHandlerInternal(IEventHandler instance)
{ {
foreach (IEventList list in _eventLists.Values) foreach (IEventList list in _eventLists.Values)
list.RemoveHandlers(instance); list.RemoveHandlers(instance);
} }
private Dictionary<Assembly, HashSet<IEventHandler>> _registeredHandlers = new Dictionary<Assembly, HashSet<IEventHandler>>();
/// <inheritdoc/> /// <inheritdoc/>
public EventManager(ITorchBase torchInstance) : base(torchInstance) public EventManager(ITorchBase torchInstance) : base(torchInstance)
{ {
} }
/// <summary>
/// Registers all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to register</param>
/// <returns><b>true</b> if added, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public bool RegisterHandler(IEventHandler handler)
{
Assembly caller = Assembly.GetCallingAssembly();
lock (_registeredHandlers)
{
if (!_registeredHandlers.TryGetValue(caller, out HashSet<IEventHandler> handlers))
_registeredHandlers.Add(caller, handlers = new HashSet<IEventHandler>());
if (handlers.Add(handler))
{
RegisterHandlerInternal(handler);
return true;
}
return false;
}
}
/// <summary>
/// Unregisters all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to unregister</param>
/// <returns><b>true</b> if removed, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public bool UnregisterHandler(IEventHandler handler)
{
Assembly caller = Assembly.GetCallingAssembly();
lock (_registeredHandlers)
{
if (!_registeredHandlers.TryGetValue(caller, out HashSet<IEventHandler> handlers))
return false;
if (handlers.Remove(handler))
{
UnregisterHandlerInternal(handler);
return true;
}
return false;
}
}
/// <summary>
/// Unregisters all handlers owned by the given assembly.
/// </summary>
/// <param name="asm">Assembly to unregister</param>
/// <param name="callback">Optional callback invoked for every registered handler in the assembly. Ignored if null</param>
/// <returns>the number of handlers that were unregistered</returns>
internal int UnregisterAllHandlers(Assembly asm, Func<IEventHandler> callback = null)
{
lock (_registeredHandlers)
{
if (!_registeredHandlers.TryGetValue(asm, out HashSet<IEventHandler> handlers))
return 0;
foreach (IEventHandler k in handlers)
{
callback?.Invoke();
UnregisterHandlerInternal(k);
}
int count = handlers.Count;
handlers.Clear();
_registeredHandlers.Remove(asm);
return count;
}
}
} }
} }

View File

@@ -6,6 +6,8 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Sandbox.Game.Entities; using Sandbox.Game.Entities;
using Sandbox.Game.Entities.Blocks; using Sandbox.Game.Entities.Blocks;
using Torch.API.Managers.Event;
using Torch.Managers.Event;
using Torch.Managers.PatchManager; using Torch.Managers.PatchManager;
using Torch.Utils; using Torch.Utils;

View File

@@ -1,11 +1,7 @@
using System; using System.Reflection;
using System.Collections.Generic; using Torch.API.Managers.Event;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Managers.EventManager namespace Torch.Managers.Event
{ {
/// <summary> /// <summary>
/// Represents the interface for adding and removing from an ordered list of callbacks. /// Represents the interface for adding and removing from an ordered list of callbacks.
@@ -17,13 +13,13 @@ namespace Torch.Managers.EventManager
/// </summary> /// </summary>
/// <param name="method">Handler method</param> /// <param name="method">Handler method</param>
/// <param name="instance">Instance to invoke the handler on</param> /// <param name="instance">Instance to invoke the handler on</param>
void AddHandler(MethodInfo method, object instance); void AddHandler(MethodInfo method, IEventHandler instance);
/// <summary> /// <summary>
/// Removes all event handlers invoked on the given instance. /// Removes all event handlers invoked on the given instance.
/// </summary> /// </summary>
/// <param name="instance">Instance to remove event handlers for</param> /// <param name="instance">Instance to remove event handlers for</param>
/// <returns>The number of event handlers removed</returns> /// <returns>The number of event handlers removed</returns>
int RemoveHandlers(object instance); int RemoveHandlers(IEventHandler instance);
} }
} }