From dcb15f967a6256a898a2b027bc3074acb0483be9 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 19 Sep 2022 14:29:21 -0700 Subject: [PATCH 1/9] Remove locks from COM events delegate management. This removes locks and instead assumes the collection is immutable. --- .../InteropServices/ComEventsMethod.cs | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 9da0795ec6e033..c89e659635d2f0 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Threading; using System.Reflection; namespace System.Runtime.InteropServices @@ -99,7 +100,7 @@ private void PreProcessSignature() /// Since multicast delegate's built-in chaining supports only chaining instances of the same type, /// we need to complement this design by using an explicit linked list data structure. /// - private readonly List _delegateWrappers = new List(); + private List _delegateWrappers = new List(); private readonly int _dispid; private ComEventsMethod? _next; @@ -156,19 +157,21 @@ public bool Empty { get { - lock (_delegateWrappers) - { - return _delegateWrappers.Count == 0; - } + var wrappers = _delegateWrappers; + return wrappers.Count == 0; } } public void AddDelegate(Delegate d, bool wrapArgs = false) { - lock (_delegateWrappers) + List wrappers; + List newWrappers; + do { + wrappers = _delegateWrappers; + // Update an existing delegate wrapper - foreach (DelegateWrapper wrapper in _delegateWrappers) + foreach (DelegateWrapper wrapper in wrappers) { if (wrapper.Delegate.GetType() == d.GetType() && wrapper.WrapArgs == wrapArgs) { @@ -177,21 +180,28 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) } } - var newWrapper = new DelegateWrapper(d, wrapArgs); - _delegateWrappers.Add(newWrapper); - } + newWrappers = wrappers.Count == 0 + ? new List() + : wrappers.GetRange(0, wrappers.Count); + + newWrappers.Add(new DelegateWrapper(d, wrapArgs)); + } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegate(Delegate d, bool wrapArgs = false) { - lock (_delegateWrappers) + List wrappers; + List newWrappers; + do { + wrappers = _delegateWrappers; + // Find delegate wrapper index int removeIdx = -1; DelegateWrapper? wrapper = null; - for (int i = 0; i < _delegateWrappers.Count; i++) + for (int i = 0; i < wrappers.Count; i++) { - DelegateWrapper wrapperMaybe = _delegateWrappers[i]; + DelegateWrapper wrapperMaybe = wrappers[i]; if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) { removeIdx = i; @@ -211,23 +221,27 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) if (newDelegate != null) { wrapper.Delegate = newDelegate; + return; // No need to update collection } - else - { - _delegateWrappers.RemoveAt(removeIdx); - } - } + + newWrappers = wrappers.GetRange(0, wrappers.Count); + newWrappers.RemoveAt(removeIdx); + } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegates(Func condition) { - lock (_delegateWrappers) + List wrappers; + List newWrappers; + do { + wrappers = _delegateWrappers; + // Find delegate wrapper indexes. Iterate in reverse such that the list to remove is sorted by high to low index. List toRemove = new List(); - for (int i = _delegateWrappers.Count - 1; i >= 0; i--) + for (int i = wrappers.Count - 1; i >= 0; i--) { - DelegateWrapper wrapper = _delegateWrappers[i]; + DelegateWrapper wrapper = wrappers[i]; Delegate[] invocationList = wrapper.Delegate.GetInvocationList(); foreach (Delegate delegateMaybe in invocationList) { @@ -246,11 +260,18 @@ public void RemoveDelegates(Func condition) } } + if (toRemove.Count == 0) + { + // Nothing to remove + return; + } + + newWrappers = wrappers.GetRange(0, wrappers.Count); foreach (int idx in toRemove) { - _delegateWrappers.RemoveAt(idx); + newWrappers.RemoveAt(idx); } - } + } while (!PublishNewWrappers(newWrappers, wrappers)); } public object? Invoke(object[] args) @@ -258,15 +279,19 @@ public void RemoveDelegates(Func condition) Debug.Assert(!Empty); object? result = null; - lock (_delegateWrappers) + var wrappers = _delegateWrappers; + foreach (DelegateWrapper wrapper in wrappers) { - foreach (DelegateWrapper wrapper in _delegateWrappers) - { - result = wrapper.Invoke(args); - } + result = wrapper.Invoke(args); } return result; } + + // Attempt to update the member wrapper field + private bool PublishNewWrappers(List newWrappers, List currentMaybe) + { + return Interlocked.CompareExchange(ref _delegateWrappers, newWrappers, currentMaybe) == currentMaybe; + } } } From 281af2738bf32592e71d0a258a43f4a1cd76ffb3 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Mon, 19 Sep 2022 15:34:32 -0700 Subject: [PATCH 2/9] Remove uses of var. --- .../src/System/Runtime/InteropServices/ComEventsMethod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index c89e659635d2f0..1c2e571231b926 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -157,7 +157,7 @@ public bool Empty { get { - var wrappers = _delegateWrappers; + List wrappers = _delegateWrappers; return wrappers.Count == 0; } } @@ -279,7 +279,7 @@ public void RemoveDelegates(Func condition) Debug.Assert(!Empty); object? result = null; - var wrappers = _delegateWrappers; + List wrappers = _delegateWrappers; foreach (DelegateWrapper wrapper in wrappers) { result = wrapper.Invoke(args); From a8f94f4cde2d6bc65b9a078b4546876aecb22132 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 20 Sep 2022 06:42:23 -0700 Subject: [PATCH 3/9] Use array instead of List --- .../InteropServices/ComEventsMethod.cs | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 1c2e571231b926..68e735b0cdae25 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -100,7 +100,7 @@ private void PreProcessSignature() /// Since multicast delegate's built-in chaining supports only chaining instances of the same type, /// we need to complement this design by using an explicit linked list data structure. /// - private List _delegateWrappers = new List(); + private DelegateWrapper[] _delegateWrappers = Array.Empty(); private readonly int _dispid; private ComEventsMethod? _next; @@ -157,15 +157,15 @@ public bool Empty { get { - List wrappers = _delegateWrappers; - return wrappers.Count == 0; + DelegateWrapper[] wrappers = _delegateWrappers; + return wrappers.Length == 0; } } public void AddDelegate(Delegate d, bool wrapArgs = false) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; @@ -180,18 +180,17 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) } } - newWrappers = wrappers.Count == 0 - ? new List() - : wrappers.GetRange(0, wrappers.Count); + newWrappers = new DelegateWrapper[wrappers.Length + 1]; + wrappers.CopyTo(newWrappers, 0); - newWrappers.Add(new DelegateWrapper(d, wrapArgs)); + newWrappers[newWrappers.Length - 1] = new DelegateWrapper(d, wrapArgs); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegate(Delegate d, bool wrapArgs = false) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; @@ -199,7 +198,7 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) // Find delegate wrapper index int removeIdx = -1; DelegateWrapper? wrapper = null; - for (int i = 0; i < wrappers.Count; i++) + for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapperMaybe = wrappers[i]; if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) @@ -224,22 +223,22 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) return; // No need to update collection } - newWrappers = wrappers.GetRange(0, wrappers.Count); - newWrappers.RemoveAt(removeIdx); + newWrappers = new DelegateWrapper[wrappers.Length - 1]; + wrappers.AsSpan(0, removeIdx).CopyTo(newWrappers); + wrappers.AsSpan(removeIdx + 1).CopyTo(newWrappers.AsSpan(removeIdx)); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegates(Func condition) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; - // Find delegate wrapper indexes. Iterate in reverse such that the list to remove is sorted by high to low index. List toRemove = new List(); - for (int i = wrappers.Count - 1; i >= 0; i--) + for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapper = wrappers[i]; Delegate[] invocationList = wrapper.Delegate.GetInvocationList(); @@ -266,12 +265,35 @@ public void RemoveDelegates(Func condition) return; } - newWrappers = wrappers.GetRange(0, wrappers.Count); - foreach (int idx in toRemove) + newWrappers = RemoveAll(wrappers, CollectionsMarshal.AsSpan(toRemove)); + } while (!PublishNewWrappers(newWrappers, wrappers)); + + // The list of indices is assumed to be sorted in ascending order + static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan toRemove) + { + // Allocate new collection + var newCol = new DelegateWrapper[oldCol.Length - toRemove.Length]; + if (newCol.Length == 0) { - newWrappers.RemoveAt(idx); + return newCol; } - } while (!PublishNewWrappers(newWrappers, wrappers)); + + // Iterate over collection, skipping elements that should be removed. + int ri = 0; + for (int oi = 0, ni = 0; oi < oldCol.Length; oi++) + { + if (ri < toRemove.Length && oi == toRemove[ri]) + { + ri++; + continue; + } + + newCol[ni] = oldCol[oi]; + ni++; + } + + return newCol; + } } public object? Invoke(object[] args) @@ -279,7 +301,7 @@ public void RemoveDelegates(Func condition) Debug.Assert(!Empty); object? result = null; - List wrappers = _delegateWrappers; + DelegateWrapper[] wrappers = _delegateWrappers; foreach (DelegateWrapper wrapper in wrappers) { result = wrapper.Invoke(args); @@ -289,7 +311,7 @@ public void RemoveDelegates(Func condition) } // Attempt to update the member wrapper field - private bool PublishNewWrappers(List newWrappers, List currentMaybe) + private bool PublishNewWrappers(DelegateWrapper[] newWrappers, DelegateWrapper[] currentMaybe) { return Interlocked.CompareExchange(ref _delegateWrappers, newWrappers, currentMaybe) == currentMaybe; } From 65f3e24e6ed8f6c6b3c1d773cba96155280ccc1b Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 20 Sep 2022 06:42:43 -0700 Subject: [PATCH 4/9] Revert "Use array instead of List" This reverts commit 5ba348f0a70ced13d8abc3d5fe38be398ad98154. --- .../InteropServices/ComEventsMethod.cs | 70 +++++++------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 68e735b0cdae25..1c2e571231b926 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -100,7 +100,7 @@ private void PreProcessSignature() /// Since multicast delegate's built-in chaining supports only chaining instances of the same type, /// we need to complement this design by using an explicit linked list data structure. /// - private DelegateWrapper[] _delegateWrappers = Array.Empty(); + private List _delegateWrappers = new List(); private readonly int _dispid; private ComEventsMethod? _next; @@ -157,15 +157,15 @@ public bool Empty { get { - DelegateWrapper[] wrappers = _delegateWrappers; - return wrappers.Length == 0; + List wrappers = _delegateWrappers; + return wrappers.Count == 0; } } public void AddDelegate(Delegate d, bool wrapArgs = false) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + List wrappers; + List newWrappers; do { wrappers = _delegateWrappers; @@ -180,17 +180,18 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) } } - newWrappers = new DelegateWrapper[wrappers.Length + 1]; - wrappers.CopyTo(newWrappers, 0); + newWrappers = wrappers.Count == 0 + ? new List() + : wrappers.GetRange(0, wrappers.Count); - newWrappers[newWrappers.Length - 1] = new DelegateWrapper(d, wrapArgs); + newWrappers.Add(new DelegateWrapper(d, wrapArgs)); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegate(Delegate d, bool wrapArgs = false) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + List wrappers; + List newWrappers; do { wrappers = _delegateWrappers; @@ -198,7 +199,7 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) // Find delegate wrapper index int removeIdx = -1; DelegateWrapper? wrapper = null; - for (int i = 0; i < wrappers.Length; i++) + for (int i = 0; i < wrappers.Count; i++) { DelegateWrapper wrapperMaybe = wrappers[i]; if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) @@ -223,22 +224,22 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) return; // No need to update collection } - newWrappers = new DelegateWrapper[wrappers.Length - 1]; - wrappers.AsSpan(0, removeIdx).CopyTo(newWrappers); - wrappers.AsSpan(removeIdx + 1).CopyTo(newWrappers.AsSpan(removeIdx)); + newWrappers = wrappers.GetRange(0, wrappers.Count); + newWrappers.RemoveAt(removeIdx); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegates(Func condition) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + List wrappers; + List newWrappers; do { wrappers = _delegateWrappers; + // Find delegate wrapper indexes. Iterate in reverse such that the list to remove is sorted by high to low index. List toRemove = new List(); - for (int i = 0; i < wrappers.Length; i++) + for (int i = wrappers.Count - 1; i >= 0; i--) { DelegateWrapper wrapper = wrappers[i]; Delegate[] invocationList = wrapper.Delegate.GetInvocationList(); @@ -265,35 +266,12 @@ public void RemoveDelegates(Func condition) return; } - newWrappers = RemoveAll(wrappers, CollectionsMarshal.AsSpan(toRemove)); - } while (!PublishNewWrappers(newWrappers, wrappers)); - - // The list of indices is assumed to be sorted in ascending order - static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan toRemove) - { - // Allocate new collection - var newCol = new DelegateWrapper[oldCol.Length - toRemove.Length]; - if (newCol.Length == 0) - { - return newCol; - } - - // Iterate over collection, skipping elements that should be removed. - int ri = 0; - for (int oi = 0, ni = 0; oi < oldCol.Length; oi++) + newWrappers = wrappers.GetRange(0, wrappers.Count); + foreach (int idx in toRemove) { - if (ri < toRemove.Length && oi == toRemove[ri]) - { - ri++; - continue; - } - - newCol[ni] = oldCol[oi]; - ni++; + newWrappers.RemoveAt(idx); } - - return newCol; - } + } while (!PublishNewWrappers(newWrappers, wrappers)); } public object? Invoke(object[] args) @@ -301,7 +279,7 @@ static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan t Debug.Assert(!Empty); object? result = null; - DelegateWrapper[] wrappers = _delegateWrappers; + List wrappers = _delegateWrappers; foreach (DelegateWrapper wrapper in wrappers) { result = wrapper.Invoke(args); @@ -311,7 +289,7 @@ static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan t } // Attempt to update the member wrapper field - private bool PublishNewWrappers(DelegateWrapper[] newWrappers, DelegateWrapper[] currentMaybe) + private bool PublishNewWrappers(List newWrappers, List currentMaybe) { return Interlocked.CompareExchange(ref _delegateWrappers, newWrappers, currentMaybe) == currentMaybe; } From 95775d798fed26f8d6c5568acc724dc7227f5994 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 21 Sep 2022 07:06:24 -0700 Subject: [PATCH 5/9] Revert "Revert "Use array instead of List"" This reverts commit aa3dddd122920633e2c5e406ae92dc223c4a5f25. --- .../InteropServices/ComEventsMethod.cs | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 1c2e571231b926..68e735b0cdae25 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -100,7 +100,7 @@ private void PreProcessSignature() /// Since multicast delegate's built-in chaining supports only chaining instances of the same type, /// we need to complement this design by using an explicit linked list data structure. /// - private List _delegateWrappers = new List(); + private DelegateWrapper[] _delegateWrappers = Array.Empty(); private readonly int _dispid; private ComEventsMethod? _next; @@ -157,15 +157,15 @@ public bool Empty { get { - List wrappers = _delegateWrappers; - return wrappers.Count == 0; + DelegateWrapper[] wrappers = _delegateWrappers; + return wrappers.Length == 0; } } public void AddDelegate(Delegate d, bool wrapArgs = false) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; @@ -180,18 +180,17 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) } } - newWrappers = wrappers.Count == 0 - ? new List() - : wrappers.GetRange(0, wrappers.Count); + newWrappers = new DelegateWrapper[wrappers.Length + 1]; + wrappers.CopyTo(newWrappers, 0); - newWrappers.Add(new DelegateWrapper(d, wrapArgs)); + newWrappers[newWrappers.Length - 1] = new DelegateWrapper(d, wrapArgs); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegate(Delegate d, bool wrapArgs = false) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; @@ -199,7 +198,7 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) // Find delegate wrapper index int removeIdx = -1; DelegateWrapper? wrapper = null; - for (int i = 0; i < wrappers.Count; i++) + for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapperMaybe = wrappers[i]; if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) @@ -224,22 +223,22 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) return; // No need to update collection } - newWrappers = wrappers.GetRange(0, wrappers.Count); - newWrappers.RemoveAt(removeIdx); + newWrappers = new DelegateWrapper[wrappers.Length - 1]; + wrappers.AsSpan(0, removeIdx).CopyTo(newWrappers); + wrappers.AsSpan(removeIdx + 1).CopyTo(newWrappers.AsSpan(removeIdx)); } while (!PublishNewWrappers(newWrappers, wrappers)); } public void RemoveDelegates(Func condition) { - List wrappers; - List newWrappers; + DelegateWrapper[] wrappers; + DelegateWrapper[] newWrappers; do { wrappers = _delegateWrappers; - // Find delegate wrapper indexes. Iterate in reverse such that the list to remove is sorted by high to low index. List toRemove = new List(); - for (int i = wrappers.Count - 1; i >= 0; i--) + for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapper = wrappers[i]; Delegate[] invocationList = wrapper.Delegate.GetInvocationList(); @@ -266,12 +265,35 @@ public void RemoveDelegates(Func condition) return; } - newWrappers = wrappers.GetRange(0, wrappers.Count); - foreach (int idx in toRemove) + newWrappers = RemoveAll(wrappers, CollectionsMarshal.AsSpan(toRemove)); + } while (!PublishNewWrappers(newWrappers, wrappers)); + + // The list of indices is assumed to be sorted in ascending order + static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan toRemove) + { + // Allocate new collection + var newCol = new DelegateWrapper[oldCol.Length - toRemove.Length]; + if (newCol.Length == 0) { - newWrappers.RemoveAt(idx); + return newCol; } - } while (!PublishNewWrappers(newWrappers, wrappers)); + + // Iterate over collection, skipping elements that should be removed. + int ri = 0; + for (int oi = 0, ni = 0; oi < oldCol.Length; oi++) + { + if (ri < toRemove.Length && oi == toRemove[ri]) + { + ri++; + continue; + } + + newCol[ni] = oldCol[oi]; + ni++; + } + + return newCol; + } } public object? Invoke(object[] args) @@ -279,7 +301,7 @@ public void RemoveDelegates(Func condition) Debug.Assert(!Empty); object? result = null; - List wrappers = _delegateWrappers; + DelegateWrapper[] wrappers = _delegateWrappers; foreach (DelegateWrapper wrapper in wrappers) { result = wrapper.Invoke(args); @@ -289,7 +311,7 @@ public void RemoveDelegates(Func condition) } // Attempt to update the member wrapper field - private bool PublishNewWrappers(List newWrappers, List currentMaybe) + private bool PublishNewWrappers(DelegateWrapper[] newWrappers, DelegateWrapper[] currentMaybe) { return Interlocked.CompareExchange(ref _delegateWrappers, newWrappers, currentMaybe) == currentMaybe; } From f202cc74755079c9ef07e57c7641c0bbfaf8c84c Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 21 Sep 2022 08:49:09 -0700 Subject: [PATCH 6/9] Remove usage of `Delegate.Combine`. Upon deeper inspection there doesn't seem to be any value to using that mechanism. An added bonus is that it simplifies the logic. --- .../InteropServices/ComEventsMethod.cs | 38 ++----------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 68e735b0cdae25..01c46d5bf5b769 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -170,16 +170,6 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) { wrappers = _delegateWrappers; - // Update an existing delegate wrapper - foreach (DelegateWrapper wrapper in wrappers) - { - if (wrapper.Delegate.GetType() == d.GetType() && wrapper.WrapArgs == wrapArgs) - { - wrapper.Delegate = Delegate.Combine(wrapper.Delegate, d); - return; - } - } - newWrappers = new DelegateWrapper[wrappers.Length + 1]; wrappers.CopyTo(newWrappers, 0); @@ -197,14 +187,12 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) // Find delegate wrapper index int removeIdx = -1; - DelegateWrapper? wrapper = null; for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapperMaybe = wrappers[i]; if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) { removeIdx = i; - wrapper = wrapperMaybe; break; } } @@ -215,14 +203,6 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) return; } - // Update wrapper or remove from collection - Delegate? newDelegate = Delegate.Remove(wrapper!.Delegate, d); - if (newDelegate != null) - { - wrapper.Delegate = newDelegate; - return; // No need to update collection - } - newWrappers = new DelegateWrapper[wrappers.Length - 1]; wrappers.AsSpan(0, removeIdx).CopyTo(newWrappers); wrappers.AsSpan(removeIdx + 1).CopyTo(newWrappers.AsSpan(removeIdx)); @@ -237,25 +217,13 @@ public void RemoveDelegates(Func condition) { wrappers = _delegateWrappers; - List toRemove = new List(); + List toRemove = new(); for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapper = wrappers[i]; - Delegate[] invocationList = wrapper.Delegate.GetInvocationList(); - foreach (Delegate delegateMaybe in invocationList) + if (condition(wrapper.Delegate)) { - if (condition(delegateMaybe)) - { - Delegate? newDelegate = Delegate.Remove(wrapper!.Delegate, delegateMaybe); - if (newDelegate != null) - { - wrapper.Delegate = newDelegate; - } - else - { - toRemove.Add(i); - } - } + toRemove.Add(i); } } From cab29e52a43ae3c7afa4a02255c479f5f3fae01f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 21 Sep 2022 10:50:05 -0700 Subject: [PATCH 7/9] Use some sugar --- .../src/System/Runtime/InteropServices/ComEventsMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 01c46d5bf5b769..337b0122a6dcf5 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -173,7 +173,7 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) newWrappers = new DelegateWrapper[wrappers.Length + 1]; wrappers.CopyTo(newWrappers, 0); - newWrappers[newWrappers.Length - 1] = new DelegateWrapper(d, wrapArgs); + newWrappers[^1] = new DelegateWrapper(d, wrapArgs); } while (!PublishNewWrappers(newWrappers, wrappers)); } From a2cc1ee2afbbcbe2bb7c0ead8de7884c2bbb82c8 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 21 Sep 2022 12:16:15 -0700 Subject: [PATCH 8/9] Simplify the RemoveAll scenario. --- .../InteropServices/ComEventsMethod.cs | 53 ++----------------- 1 file changed, 5 insertions(+), 48 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 337b0122a6dcf5..b8ffe7e2bd3f5b 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -169,10 +169,8 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) do { wrappers = _delegateWrappers; - newWrappers = new DelegateWrapper[wrappers.Length + 1]; wrappers.CopyTo(newWrappers, 0); - newWrappers[^1] = new DelegateWrapper(d, wrapArgs); } while (!PublishNewWrappers(newWrappers, wrappers)); } @@ -190,7 +188,7 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) for (int i = 0; i < wrappers.Length; i++) { DelegateWrapper wrapperMaybe = wrappers[i]; - if (wrapperMaybe.Delegate.GetType() == d.GetType() && wrapperMaybe.WrapArgs == wrapArgs) + if (wrapperMaybe.Delegate == d && wrapperMaybe.WrapArgs == wrapArgs) { removeIdx = i; break; @@ -216,52 +214,11 @@ public void RemoveDelegates(Func condition) do { wrappers = _delegateWrappers; - - List toRemove = new(); - for (int i = 0; i < wrappers.Length; i++) - { - DelegateWrapper wrapper = wrappers[i]; - if (condition(wrapper.Delegate)) - { - toRemove.Add(i); - } - } - - if (toRemove.Count == 0) - { - // Nothing to remove - return; - } - - newWrappers = RemoveAll(wrappers, CollectionsMarshal.AsSpan(toRemove)); - } while (!PublishNewWrappers(newWrappers, wrappers)); - - // The list of indices is assumed to be sorted in ascending order - static DelegateWrapper[] RemoveAll(DelegateWrapper[] oldCol, ReadOnlySpan toRemove) - { - // Allocate new collection - var newCol = new DelegateWrapper[oldCol.Length - toRemove.Length]; - if (newCol.Length == 0) - { - return newCol; - } - - // Iterate over collection, skipping elements that should be removed. - int ri = 0; - for (int oi = 0, ni = 0; oi < oldCol.Length; oi++) - { - if (ri < toRemove.Length && oi == toRemove[ri]) - { - ri++; - continue; - } - - newCol[ni] = oldCol[oi]; - ni++; - } - - return newCol; + List tmp = new(wrappers); + tmp.RemoveAll(w => condition(w.Delegate)); + newWrappers = tmp.ToArray(); } + while (!PublishNewWrappers(newWrappers, wrappers)); } public object? Invoke(object[] args) From d7c3b4383ec745cda0ec983dd2e42beafd9889b2 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 22 Sep 2022 08:53:54 -0700 Subject: [PATCH 9/9] Style nits. --- .../Runtime/InteropServices/ComEventsMethod.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index b8ffe7e2bd3f5b..9ffd267a881a4d 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -155,17 +155,12 @@ public static ComEventsMethod Add(ComEventsMethod? methods, ComEventsMethod meth public bool Empty { - get - { - DelegateWrapper[] wrappers = _delegateWrappers; - return wrappers.Length == 0; - } + get => _delegateWrappers.Length == 0; } public void AddDelegate(Delegate d, bool wrapArgs = false) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + DelegateWrapper[] wrappers, newWrappers; do { wrappers = _delegateWrappers; @@ -177,8 +172,7 @@ public void AddDelegate(Delegate d, bool wrapArgs = false) public void RemoveDelegate(Delegate d, bool wrapArgs = false) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + DelegateWrapper[] wrappers, newWrappers; do { wrappers = _delegateWrappers; @@ -209,8 +203,7 @@ public void RemoveDelegate(Delegate d, bool wrapArgs = false) public void RemoveDelegates(Func condition) { - DelegateWrapper[] wrappers; - DelegateWrapper[] newWrappers; + DelegateWrapper[] wrappers, newWrappers; do { wrappers = _delegateWrappers; @@ -226,8 +219,7 @@ public void RemoveDelegates(Func condition) Debug.Assert(!Empty); object? result = null; - DelegateWrapper[] wrappers = _delegateWrappers; - foreach (DelegateWrapper wrapper in wrappers) + foreach (DelegateWrapper wrapper in _delegateWrappers) { result = wrapper.Invoke(args); }