Skip to content

Commit 6334240

Browse files
committed
[Core] Reworked EventInvoker
1 parent e7c7c8f commit 6334240

1 file changed

Lines changed: 69 additions & 45 deletions

File tree

Lagrange.Core/Events/EventInvoker.cs

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,42 @@ public sealed class EventInvoker(BotContext context) : IDisposable
1111
{
1212
private const string Tag = nameof(EventInvoker);
1313

14-
private readonly ConcurrentDictionary<Type, Delegate> _syncHandlers = new();
15-
private readonly ConcurrentDictionary<Type, Delegate> _asyncHandlers = new();
14+
private static readonly ConcurrentDictionary<Delegate, Delegate> SyncHandlerCache = new();
15+
private static readonly ConcurrentDictionary<Delegate, Delegate> AsyncHandlerCache = new();
16+
17+
private readonly ConcurrentDictionary<Type, Delegate?> _syncHandlers = new();
18+
private readonly ConcurrentDictionary<Type, Delegate?> _asyncHandlers = new();
1619

1720
public delegate void LagrangeEventHandler <in TEvent>(BotContext ctx, TEvent e) where TEvent : EventBase;
1821
public delegate Task LagrangeAsyncEventHandler<in TEvent>(BotContext ctx, TEvent e) where TEvent : EventBase;
1922

2023
public void RegisterEvent<TEvent>(LagrangeEventHandler<TEvent> handler) where TEvent : EventBase
2124
{
22-
Debug.Assert(!handler.Method.IsStatic);
25+
Debug.Assert(handler.Method.IsStatic is false);
2326

24-
AddHandler(_syncHandlers, typeof(TEvent), BuildSyncDelegate(handler));
27+
var wrappedHandler = BuildSyncDelegate(handler);
28+
AddHandler(_syncHandlers, typeof(TEvent), wrappedHandler);
2529
}
2630

2731
public void RegisterEvent<TEvent>(LagrangeAsyncEventHandler<TEvent> handler) where TEvent : EventBase
2832
{
29-
Debug.Assert(!handler.Method.IsStatic);
33+
Debug.Assert(handler.Method.IsStatic is false);
3034

31-
AddHandler(_asyncHandlers, typeof(TEvent), BuildAsyncDelegate(handler));
35+
var wrappedHandler = BuildAsyncDelegate(handler);
36+
AddHandler(_asyncHandlers, typeof(TEvent), wrappedHandler);
3237
}
3338

34-
public void UnregisterEvent<TEvent>(LagrangeEventHandler<TEvent> handler) where TEvent : EventBase => RemoveHandler(_syncHandlers, typeof(TEvent), handler);
39+
public void UnregisterEvent<TEvent>(LagrangeEventHandler<TEvent> handler) where TEvent : EventBase
40+
{
41+
var wrappedHandler = BuildSyncDelegate(handler);
42+
RemoveHandler(_syncHandlers, typeof(TEvent), wrappedHandler);
43+
}
3544

36-
public void UnregisterEvent<TEvent>(LagrangeAsyncEventHandler<TEvent> handler) where TEvent : EventBase => RemoveHandler(_asyncHandlers, typeof(TEvent), handler);
45+
public void UnregisterEvent<TEvent>(LagrangeAsyncEventHandler<TEvent> handler) where TEvent : EventBase
46+
{
47+
var wrappedHandler = BuildAsyncDelegate(handler);
48+
RemoveHandler(_asyncHandlers, typeof(TEvent), wrappedHandler);
49+
}
3750

3851
public void UnregisterEvent<TEvent>() where TEvent : EventBase
3952
{
@@ -47,12 +60,12 @@ internal void PostEvent<T>(T ev) where T : EventBase => Task.Run(async () =>
4760

4861
try
4962
{
50-
if (_syncHandlers.TryGetValue(typeof(T), out var @delegate))
63+
if (_syncHandlers.TryGetValue(typeof(T), out var @delegate) && @delegate is not null)
5164
{
5265
((Action<BotContext, EventBase>)@delegate).Invoke(context, ev);
5366
}
5467

55-
if (_asyncHandlers.TryGetValue(typeof(T), out var asyncDelegate))
68+
if (_asyncHandlers.TryGetValue(typeof(T), out var asyncDelegate) && asyncDelegate is not null)
5669
{
5770
await ((Func<BotContext, EventBase, Task>)asyncDelegate)(context, ev);
5871
}
@@ -74,55 +87,66 @@ public void Dispose()
7487
_asyncHandlers.Clear();
7588
}
7689

77-
private static void AddHandler(ConcurrentDictionary<Type, Delegate> dict, Type key, Delegate handler)
90+
private static void AddHandler(ConcurrentDictionary<Type, Delegate?> dict, Type key, Delegate handler)
7891
{
7992
dict.AddOrUpdate(key, handler, (_, old) => Delegate.Combine(old, handler));
8093
}
8194

82-
private static void RemoveHandler(ConcurrentDictionary<Type, Delegate> dict, Type key, Delegate handler)
95+
private static void RemoveHandler(ConcurrentDictionary<Type, Delegate?> dict, Type key, Delegate handler)
8396
{
84-
dict.AddOrUpdate(key, _ => null!, (_, old) =>
85-
{
86-
var updated = Delegate.Remove(old, handler);
87-
return updated != null && updated.GetInvocationList().Length > 0 ? updated : null!;
88-
});
97+
dict.AddOrUpdate(key, _ => null, (_, old) => Delegate.Remove(old, handler)!);
8998

90-
if (dict.TryRemove(key, out var d) && d is not { }) dict.TryRemove(key, out _); // is not { } is needed to avoid removing the key if the delegate is null, and escape from ReSharper's warning
99+
if (dict.TryGetValue(key, out var currentDelegate) && currentDelegate == null)
100+
{
101+
dict.TryRemove(key, out _);
102+
}
91103
}
92104

93105
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "DynamicMethod is not supported in AOT, but this branch is skipped there")]
94-
private static Action<BotContext, EventBase> BuildSyncDelegate<TEvent>(LagrangeEventHandler<TEvent> h) where TEvent : EventBase
106+
private static Delegate BuildSyncDelegate<TEvent>(LagrangeEventHandler<TEvent> h) where TEvent : EventBase
95107
{
96-
if (!RuntimeFeature.IsDynamicCodeSupported) return (ctx, ev) => h(ctx, (TEvent)ev);
108+
return SyncHandlerCache.GetOrAdd(h, handler =>
109+
{
110+
if (!RuntimeFeature.IsDynamicCodeSupported)
111+
{
112+
return (Action<BotContext, EventBase>)((ctx, ev) => ((LagrangeEventHandler<TEvent>)handler)(ctx, (TEvent)ev));
113+
}
97114

98-
var dm = new DynamicMethod($"EventInvoker_{typeof(TEvent).Name}", typeof(void), [h.Target!.GetType(), typeof(BotContext), typeof(EventBase)], typeof(EventInvoker), true);
99-
var il = dm.GetILGenerator();
100-
101-
il.Emit(OpCodes.Ldarg_0);
102-
il.Emit(OpCodes.Ldarg_1);
103-
il.Emit(OpCodes.Ldarg_2);
104-
il.Emit(OpCodes.Castclass, typeof(TEvent));
105-
il.Emit(OpCodes.Callvirt, h.Method);
106-
il.Emit(OpCodes.Ret);
107-
108-
return dm.CreateDelegate<Action<BotContext, EventBase>>(h.Target);
115+
var dm = new DynamicMethod($"EventInvoker_{typeof(TEvent).Name}_{Guid.NewGuid()}", typeof(void), [handler.Target!.GetType(), typeof(BotContext), typeof(EventBase)], typeof(EventInvoker), true);
116+
var il = dm.GetILGenerator();
117+
118+
il.Emit(OpCodes.Ldarg_0);
119+
il.Emit(OpCodes.Ldarg_1);
120+
il.Emit(OpCodes.Ldarg_2);
121+
il.Emit(OpCodes.Castclass, typeof(TEvent));
122+
il.Emit(OpCodes.Callvirt, handler.Method);
123+
il.Emit(OpCodes.Ret);
124+
125+
return dm.CreateDelegate<Action<BotContext, EventBase>>(handler.Target);
126+
});
109127
}
110128

111129
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "DynamicMethod is not supported in AOT, but this branch is skipped there")]
112-
private static Func<BotContext, EventBase, Task> BuildAsyncDelegate<TEvent>(LagrangeAsyncEventHandler<TEvent> h) where TEvent : EventBase
130+
private static Delegate BuildAsyncDelegate<TEvent>(LagrangeAsyncEventHandler<TEvent> h) where TEvent : EventBase
113131
{
114-
if (!RuntimeFeature.IsDynamicCodeSupported) return (ctx, ev) => h(ctx, (TEvent)ev);
132+
return AsyncHandlerCache.GetOrAdd(h, handler =>
133+
{
134+
if (!RuntimeFeature.IsDynamicCodeSupported)
135+
{
136+
return (Func<BotContext, EventBase, Task>)((ctx, ev) => ((LagrangeAsyncEventHandler<TEvent>)handler)(ctx, (TEvent)ev));
137+
}
115138

116-
var dm = new DynamicMethod($"EventInvoker_{typeof(TEvent).Name}_Async", typeof(Task), [h.Target!.GetType(), typeof(BotContext), typeof(EventBase)], typeof(EventInvoker), true);
117-
var il = dm.GetILGenerator();
118-
119-
il.Emit(OpCodes.Ldarg_0);
120-
il.Emit(OpCodes.Ldarg_1);
121-
il.Emit(OpCodes.Ldarg_2);
122-
il.Emit(OpCodes.Castclass, typeof(TEvent));
123-
il.Emit(OpCodes.Callvirt, h.Method);
124-
il.Emit(OpCodes.Ret);
125-
126-
return dm.CreateDelegate<Func<BotContext, EventBase, Task>>(h.Target);
139+
var dm = new DynamicMethod($"EventInvoker_{typeof(TEvent).Name}_Async_{Guid.NewGuid()}", typeof(Task), [handler.Target!.GetType(), typeof(BotContext), typeof(EventBase)], typeof(EventInvoker), true);
140+
var il = dm.GetILGenerator();
141+
142+
il.Emit(OpCodes.Ldarg_0);
143+
il.Emit(OpCodes.Ldarg_1);
144+
il.Emit(OpCodes.Ldarg_2);
145+
il.Emit(OpCodes.Castclass, typeof(TEvent));
146+
il.Emit(OpCodes.Callvirt, handler.Method);
147+
il.Emit(OpCodes.Ret);
148+
149+
return dm.CreateDelegate<Func<BotContext, EventBase, Task>>(handler.Target);
150+
});
127151
}
128-
}
152+
}

0 commit comments

Comments
 (0)