From 54ec77ebfaf7504f2797835f18b87e6f74de31f3 Mon Sep 17 00:00:00 2001 From: mikedennis Date: Tue, 28 May 2019 21:20:07 -0600 Subject: [PATCH 1/5] Change node filters to use a list instead of a dictionary so insert order can be maintained. --- .../Protocol/Filters/NodeFiltersCollection.cs | 2 +- NBitcoin/Utils/ThreadSafeList.cs | 109 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 NBitcoin/Utils/ThreadSafeList.cs diff --git a/NBitcoin/Protocol/Filters/NodeFiltersCollection.cs b/NBitcoin/Protocol/Filters/NodeFiltersCollection.cs index 5f57381fa2..7e9b978c7e 100644 --- a/NBitcoin/Protocol/Filters/NodeFiltersCollection.cs +++ b/NBitcoin/Protocol/Filters/NodeFiltersCollection.cs @@ -7,7 +7,7 @@ namespace NBitcoin.Protocol.Filters { - public class NodeFiltersCollection : ThreadSafeCollection + public class NodeFiltersCollection : ThreadSafeList { public IDisposable Add(Action onReceiving, Action onSending = null) { diff --git a/NBitcoin/Utils/ThreadSafeList.cs b/NBitcoin/Utils/ThreadSafeList.cs new file mode 100644 index 0000000000..43e5d668f4 --- /dev/null +++ b/NBitcoin/Utils/ThreadSafeList.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NBitcoin +{ + public class ThreadSafeList : IEnumerable + { + private List _Behaviors; + private object _lock = new object(); + + public ThreadSafeList() + { + lock (_lock) + _Behaviors = new List(); + } + + /// + /// Add an item to the collection + /// + /// + /// When disposed, the item is removed + public IDisposable Add(T item) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); + OnAdding(item); + lock (_lock) + { + _Behaviors.Add(item); + } + return new ActionDisposable(() => + { + }, () => Remove(item)); + } + + protected virtual void OnAdding(T obj) + { + } + protected virtual void OnRemoved(T obj) + { + } + + public bool Remove(T item) + { + bool removed = false; + lock (_lock) + { + removed = _Behaviors.Remove(item); + } + + if (removed) + OnRemoved(item); + return removed; + } + + public void Clear() + { + foreach (var behavior in this) + Remove(behavior); + } + + public T FindOrCreate() where U : T, new() + { + return FindOrCreate(() => new U()); + } + public U FindOrCreate(Func create) where U : T + { + var result = this.OfType().FirstOrDefault(); + if (result == null) + { + result = create(); + Add(result); + } + return result; + } + public U Find() where U : T + { + return this.OfType().FirstOrDefault(); + } + + public void Remove() where U : T + { + foreach (var b in this.OfType()) + { + Remove(b); + } + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return _Behaviors.ToList().GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} From 494f669688b64f144e2a453a24b58bd4684b22a8 Mon Sep 17 00:00:00 2001 From: mikedennis Date: Tue, 28 May 2019 21:39:49 -0600 Subject: [PATCH 2/5] Fix code review issue --- NBitcoin/Utils/ThreadSafeList.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NBitcoin/Utils/ThreadSafeList.cs b/NBitcoin/Utils/ThreadSafeList.cs index 43e5d668f4..776d9641aa 100644 --- a/NBitcoin/Utils/ThreadSafeList.cs +++ b/NBitcoin/Utils/ThreadSafeList.cs @@ -92,7 +92,12 @@ public void Remove() where U : T public IEnumerator GetEnumerator() { - return _Behaviors.ToList().GetEnumerator(); + List list = null; + lock (_lock) + { + list = _Behaviors.ToList(); + } + return list?.GetEnumerator(); } #endregion From 4d7f30d5d6b6fc70a30fed3d19ac4d7a1f04c0a3 Mon Sep 17 00:00:00 2001 From: mikedennis Date: Sun, 2 Jun 2019 14:43:40 -0600 Subject: [PATCH 3/5] Add optimization for enumerator creation. --- NBitcoin/Utils/ThreadSafeList.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/NBitcoin/Utils/ThreadSafeList.cs b/NBitcoin/Utils/ThreadSafeList.cs index 776d9641aa..3efd9c7871 100644 --- a/NBitcoin/Utils/ThreadSafeList.cs +++ b/NBitcoin/Utils/ThreadSafeList.cs @@ -10,6 +10,8 @@ public class ThreadSafeList : IEnumerable private List _Behaviors; private object _lock = new object(); + private List _EnumeratorList = null; + public ThreadSafeList() { lock (_lock) @@ -29,6 +31,7 @@ public IDisposable Add(T item) lock (_lock) { _Behaviors.Add(item); + _EnumeratorList = null; } return new ActionDisposable(() => { @@ -48,6 +51,7 @@ public bool Remove(T item) lock (_lock) { removed = _Behaviors.Remove(item); + _EnumeratorList = null; } if (removed) @@ -92,12 +96,14 @@ public void Remove() where U : T public IEnumerator GetEnumerator() { - List list = null; - lock (_lock) + if (_EnumeratorList == null) { - list = _Behaviors.ToList(); + lock (_lock) + { + _EnumeratorList = _Behaviors.ToList(); + } } - return list?.GetEnumerator(); + return _EnumeratorList?.GetEnumerator(); } #endregion From 5a95059fcd21da17d3f01940a581cbed77184866 Mon Sep 17 00:00:00 2001 From: mikedennis Date: Mon, 3 Jun 2019 07:26:21 -0600 Subject: [PATCH 4/5] Better locking around getting the enumerator --- NBitcoin/Utils/ThreadSafeList.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/NBitcoin/Utils/ThreadSafeList.cs b/NBitcoin/Utils/ThreadSafeList.cs index 3efd9c7871..0789cbda98 100644 --- a/NBitcoin/Utils/ThreadSafeList.cs +++ b/NBitcoin/Utils/ThreadSafeList.cs @@ -96,14 +96,17 @@ public void Remove() where U : T public IEnumerator GetEnumerator() { - if (_EnumeratorList == null) + IEnumerator enumerator = null; + + lock (_lock) { - lock (_lock) - { + if (_EnumeratorList == null) _EnumeratorList = _Behaviors.ToList(); - } + + enumerator = _EnumeratorList?.GetEnumerator(); } - return _EnumeratorList?.GetEnumerator(); + + return enumerator; } #endregion From 00bbecd523025736a364a4ff0e8b7aed87175b0d Mon Sep 17 00:00:00 2001 From: mikedennis Date: Mon, 3 Jun 2019 18:46:50 -0600 Subject: [PATCH 5/5] Fix GetEnumerator --- NBitcoin/Utils/ThreadSafeList.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/NBitcoin/Utils/ThreadSafeList.cs b/NBitcoin/Utils/ThreadSafeList.cs index 0789cbda98..dc891e9fd4 100644 --- a/NBitcoin/Utils/ThreadSafeList.cs +++ b/NBitcoin/Utils/ThreadSafeList.cs @@ -96,16 +96,16 @@ public void Remove() where U : T public IEnumerator GetEnumerator() { - IEnumerator enumerator = null; - - lock (_lock) + IEnumerator enumerator = _EnumeratorList?.GetEnumerator(); + if (enumerator == null) { - if (_EnumeratorList == null) - _EnumeratorList = _Behaviors.ToList(); - - enumerator = _EnumeratorList?.GetEnumerator(); + lock (_lock) + { + var behaviorsList = _Behaviors.ToList(); + _EnumeratorList = behaviorsList; + enumerator = behaviorsList.GetEnumerator(); + } } - return enumerator; }