Skip to content

Commit

Permalink
Merge pull request #308 from Karnah/feature/optimize-work-with-listen…
Browse files Browse the repository at this point in the history
…ers-list

Optimized work with Listener list
  • Loading branch information
konne committed Aug 26, 2021
2 parents f505a4a + b410d52 commit cf693bb
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 43 deletions.
124 changes: 124 additions & 0 deletions src/Engine/ListenerList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
namespace WPFLocalizeExtension.Engine
{
#region Usings

using System;
using System.Collections.Generic;
using System.Linq;

#endregion

/// <summary>
/// Represents a collection of listeners.
/// </summary>
internal class ListenersList
{
private readonly Dictionary<WeakReference, int> listeners;
private readonly Dictionary<int, List<WeakReference>> listenersHashCodes;
private readonly List<WeakReference> deadListeners;

/// <summary>
/// Create new empty <see cref="ListenersList" /> instance.
/// </summary>
public ListenersList()
{
listeners = new Dictionary<WeakReference, int>();
listenersHashCodes = new Dictionary<int, List<WeakReference>>();
deadListeners = new List<WeakReference>();
}

/// <summary>
/// The count of listeners.
/// </summary>
public int Count => listeners.Count;

/// <summary>
/// Add new listener.
/// </summary>
public void AddListener(IDictionaryEventListener listener)
{
// Add listener if it not registered yet.
var weakReference = new WeakReference(listener);
var hashCode = listener.GetHashCode();
if (!listenersHashCodes.TryGetValue(hashCode, out var sameHashCodeListeners))
{
listeners.Add(weakReference, hashCode);
listenersHashCodes.Add(hashCode, new List<WeakReference> { weakReference });
}
else if (sameHashCodeListeners.All(wr => wr.Target != listener))
{
listeners.Add(weakReference, hashCode);
sameHashCodeListeners.Add(weakReference);
}
}

/// <summary>
/// Get all alive listeners.
/// </summary>
public IEnumerable<IDictionaryEventListener> GetListeners()
{
try
{
foreach (var listener in listeners)
{
var listenerReference = listener.Key.Target as IDictionaryEventListener;
if (listenerReference == null)
{
deadListeners.Add(listener.Key);
continue;
}

yield return listenerReference;
}
}
finally
{
// Finally block is necessary because of `yield return`.
// It guarantees this method will be called even if listeners won't enumerate till the end.
ClearDeadReferences();
}
}

/// <summary>
/// Remove listener.
/// </summary>
public void RemoveListener(IDictionaryEventListener listener)
{
var hashCode = listener.GetHashCode();
if (!listenersHashCodes.TryGetValue(hashCode, out var hashCodes))
return;

var wr = hashCodes.FirstOrDefault(l => l.Target == listener);
if (wr == null)
return;

if (hashCodes.Count > 1)
hashCodes.Remove(wr);
else
listenersHashCodes.Remove(hashCode);

listeners.Remove(wr);
}

/// <summary>
/// Clear internal list from all dead listeners.
/// </summary>
private void ClearDeadReferences()
{
if (deadListeners.Count == 0)
return;

foreach (var deadListener in deadListeners)
{
var hashCode = listeners[deadListener];
listenersHashCodes[hashCode].Remove(deadListener);
if (!listenersHashCodes[hashCode].Any())
listenersHashCodes.Remove(hashCode);

listeners.Remove(deadListener);
}

deadListeners.Clear();
}
}
}
63 changes: 20 additions & 43 deletions src/Engine/LocalizeDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ internal static class DictionaryEvent
/// <summary>
/// The list of listeners
/// </summary>
private static readonly List<WeakReference> Listeners = new List<WeakReference>();
private static readonly ListenersList Listeners = new ListenersList();
private static readonly object ListenersLock = new object();

/// <summary>
Expand All @@ -979,22 +979,25 @@ internal static class DictionaryEvent
/// <param name="args">The event arguments.</param>
internal static void Invoke(DependencyObject sender, DictionaryEventArgs args)
{
var list = new List<IDictionaryEventListener>();

lock (ListenersLock)
{
foreach (var wr in Listeners.ToList())
var exceptions = new List<Exception>();

foreach (var listener in Listeners.GetListeners())
{
var targetReference = wr.Target;
if (targetReference != null)
list.Add((IDictionaryEventListener)targetReference);
else
Listeners.Remove(wr);
try
{
listener.ResourceChanged(sender, args);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
}

foreach (var item in list)
item.ResourceChanged(sender, args);
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
}
}

/// <summary>
Expand All @@ -1005,24 +1008,10 @@ internal static void AddListener(IDictionaryEventListener listener)
{
if (listener == null)
return;

// Check, if this listener already was added.
bool listenerExists = false;


lock (ListenersLock)
{
foreach (var wr in Listeners.ToList())
{
var targetReference = wr.Target;
if (targetReference == null)
Listeners.Remove(wr);
else if (targetReference == listener)
listenerExists = true;
}

// Add it now.
if (!listenerExists)
Listeners.Add(new WeakReference(listener));
Listeners.AddListener(listener);
}
}

Expand All @@ -1037,14 +1026,7 @@ internal static void RemoveListener(IDictionaryEventListener listener)

lock (ListenersLock)
{
foreach (var wr in Listeners.ToList())
{
var targetReference = wr.Target;
if (targetReference == null)
Listeners.Remove(wr);
else if ((IDictionaryEventListener)targetReference == listener)
Listeners.Remove(wr);
}
Listeners.RemoveListener(listener);
}
}

Expand All @@ -1057,14 +1039,9 @@ internal static IEnumerable<T> EnumerateListeners<T>()
{
lock (ListenersLock)
{
foreach (var wr in Listeners.ToList())
foreach (var listener in Listeners.GetListeners().OfType<T>())
{
var targetReference = wr.Target;

if (targetReference == null)
Listeners.Remove(wr);
else if (targetReference is T)
yield return (T)targetReference;
yield return listener;
}
}
}
Expand Down

0 comments on commit cf693bb

Please sign in to comment.