From cff440539f49049225fafd997c9029ae1af36e75 Mon Sep 17 00:00:00 2001 From: Jerome Laban Date: Mon, 25 Nov 2024 15:47:33 -0500 Subject: [PATCH] chore: Adjust for thread safety, reentrancy --- .../UI/Xaml/DependencyObjectCollection.cs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Uno.UI/UI/Xaml/DependencyObjectCollection.cs b/src/Uno.UI/UI/Xaml/DependencyObjectCollection.cs index 70003d443970..b917c36c95d2 100644 --- a/src/Uno.UI/UI/Xaml/DependencyObjectCollection.cs +++ b/src/Uno.UI/UI/Xaml/DependencyObjectCollection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -25,7 +26,27 @@ public partial class DependencyObjectCollectionBase : DependencyObject public partial class DependencyObjectCollection : DependencyObjectCollectionBase, IList, IEnumerable, IEnumerable, IObservableVector where T : DependencyObject { - public event VectorChangedEventHandler VectorChanged; + // Explicit handlers list to avoid the cost of multicast delegates handling + private List> _vectorChangedHandlers; + + public event VectorChangedEventHandler VectorChanged + { + add + { + lock (_vectorChangedHandlers) + { + (_vectorChangedHandlers ??= new()).Add(value); + } + } + + remove + { + lock (_vectorChangedHandlers) + { + (_vectorChangedHandlers ??= new()).Remove(value); + } + } + } private readonly List _list = new List(); @@ -205,7 +226,32 @@ internal List.Enumerator GetEnumeratorFast() => _list.GetEnumerator(); private void RaiseVectorChanged(CollectionChange change, int index) - => VectorChanged?.Invoke(this, new VectorChangedEventArgs(change, (uint)index)); + { + if (_vectorChangedHandlers is { Count: > 0 }) + { + var args = new VectorChangedEventArgs(change, (uint)index); + + if (_vectorChangedHandlers.Count == 1) + { + _vectorChangedHandlers[0].Invoke(this, args); + } + else + { + // Clone the array to account for reentrancy. + var handlersClone = ArrayPool>.Shared.Rent(_vectorChangedHandlers.Count); + _vectorChangedHandlers.CopyTo(handlersClone, 0); + + // Use the original count, the rented array may be larger. + var count = _vectorChangedHandlers.Count; + foreach (var handler in handlersClone) + { + handler.Invoke(this, args); + } + + ArrayPool>.Shared.Return(handlersClone); + } + } + } private protected virtual void OnAdded(T d) {