Skip to content

Instantly share code, notes, and snippets.

@lbargaoanu
Created March 4, 2017 17:29
Show Gist options
  • Save lbargaoanu/c47dba44e0df965282c5efd048cfa4f6 to your computer and use it in GitHub Desktop.
Save lbargaoanu/c47dba44e0df965282c5efd048cfa4f6 to your computer and use it in GitHub Desktop.
A way to communicate between threads using events. Based on SynchronizationContext.
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace Common
{
// this is thread safe
// it avoids locking in OnFireEvent by using immutable data structures (the copy overhead is in Add
// and Remove)
// eventsList is not changed, it is copied and replaced with the updated version
// for each event, the handlers are kept in an array that is replaced on Add and Remove
public class EventsList
{
private HybridDictionary eventsLists = new HybridDictionary();
public void AddHandler<TEventArgs>(EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
{
lock(eventsLists)
{
var handlers = GetHandlers<TEventArgs>();
var runner = new EventRunner<TEventArgs>(handler);
Context context;
if(handlers == null)
{
handlers = new[] { runner };
context = Context.NewEvent;
}
else
{
handlers = handlers.Concat(Enumerable.Repeat(runner, 1)).ToArray();
context = Context.None;
}
SetNewHandlersList(handlers, context);
}
}
public void RemoveHandler<TEventArgs>(EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
{
lock(eventsLists)
{
var handlers = GetHandlers<TEventArgs>();
if(handlers == null)
{
return;
}
handlers = handlers.Where(runner => runner.OriginalCallback != handler).ToArray();
if(handlers.Length == 0)
{
handlers = null;
}
SetNewHandlersList(handlers, Context.None);
}
}
public void OnFireEvent<TEventArgs>(TEventArgs eventArgs) where TEventArgs : EventArgs
{
Debug.WriteLine("[OnFireEvent] " + typeof(TEventArgs));
var handlers = GetHandlers<TEventArgs>();
if(handlers == null)
{
return;
}
foreach(var eventRunner in handlers)
{
eventRunner.RunAsync(eventArgs);
}
}
private EventRunner<TEventArgs>[] GetHandlers<TEventArgs>() where TEventArgs : EventArgs
{
return (EventRunner<TEventArgs>[]) eventsLists[typeof(TEventArgs)];
}
private void SetNewHandlersList<TEventArgs>(EventRunner<TEventArgs>[] handlers, Context context) where TEventArgs : EventArgs
{
var size = (context == Context.NewEvent) ? eventsLists.Count + 1 : eventsLists.Count;
var newDictionary = new HybridDictionary(size);
foreach(DictionaryEntry entry in eventsLists)
{
newDictionary.Add(entry.Key, entry.Value);
}
newDictionary[typeof(TEventArgs)] = handlers;
eventsLists = newDictionary;
}
enum Context { None, NewEvent };
private struct EventRunner<TEventArgs> where TEventArgs : EventArgs
{
private readonly SynchronizationContext syncContext;
private readonly SendOrPostCallback callback;
internal readonly EventHandler<TEventArgs> OriginalCallback;
public EventRunner(EventHandler<TEventArgs> myEvent)
{
syncContext = AsyncOperationManager.SynchronizationContext;
callback = state => myEvent(null, (TEventArgs) state);
OriginalCallback = myEvent;
}
public void RunAsync(TEventArgs e)
{
syncContext.Post(callback, e);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment