From 7601da1160eac4d93a9dd06af01c6de816e15808 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 20 Sep 2019 00:28:41 +0200 Subject: [PATCH 1/2] Reduce closure allocations for priority level and entry. --- src/Avalonia.Base/PriorityBindingEntry.cs | 21 +++++----- src/Avalonia.Base/PriorityLevel.cs | 49 ++++++++++++++++------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs index 95add0dfac..312018d8ee 100644 --- a/src/Avalonia.Base/PriorityBindingEntry.cs +++ b/src/Avalonia.Base/PriorityBindingEntry.cs @@ -99,37 +99,40 @@ namespace Avalonia void IObserver.OnNext(object value) { - void Signal() + void Signal(PriorityBindingEntry instance, object newValue) { - var notification = value as BindingNotification; + var notification = newValue as BindingNotification; if (notification != null) { if (notification.HasValue || notification.ErrorType == BindingErrorType.Error) { - Value = notification.Value; - _owner.Changed(this); + instance.Value = notification.Value; + instance._owner.Changed(instance); } if (notification.ErrorType != BindingErrorType.None) { - _owner.Error(this, notification); + instance._owner.Error(instance, notification); } } else { - Value = value; - _owner.Changed(this); + instance.Value = newValue; + instance._owner.Changed(instance); } } if (Dispatcher.UIThread.CheckAccess()) { - Signal(); + Signal(this, value); } else { - Dispatcher.UIThread.Post(Signal); + var instance = this; + var newValue = value; + + Dispatcher.UIThread.Post(() => Signal(instance, newValue)); } } diff --git a/src/Avalonia.Base/PriorityLevel.cs b/src/Avalonia.Base/PriorityLevel.cs index 909558b0ce..6366911e77 100644 --- a/src/Avalonia.Base/PriorityLevel.cs +++ b/src/Avalonia.Base/PriorityLevel.cs @@ -110,20 +110,7 @@ namespace Avalonia entry.Start(binding); - return Disposable.Create(() => - { - if (!entry.HasCompleted) - { - Bindings.Remove(node); - - entry.Dispose(); - - if (entry.Index >= ActiveBindingIndex) - { - ActivateFirstBinding(); - } - } - }); + return new RemoveBindingDisposable(node, Bindings, this); } /// @@ -191,5 +178,39 @@ namespace Avalonia ActiveBindingIndex = -1; Owner.LevelValueChanged(this); } + + private sealed class RemoveBindingDisposable : IDisposable + { + private readonly LinkedListNode _binding; + private readonly LinkedList _bindings; + private readonly PriorityLevel _priorityLevel; + + public RemoveBindingDisposable( + LinkedListNode binding, + LinkedList bindings, + PriorityLevel priorityLevel) + { + _binding = binding; + _bindings = bindings; + _priorityLevel = priorityLevel; + } + + public void Dispose() + { + PriorityBindingEntry entry = _binding.Value; + + if (!entry.HasCompleted) + { + _bindings.Remove(_binding); + + entry.Dispose(); + + if (entry.Index >= _priorityLevel.ActiveBindingIndex) + { + _priorityLevel.ActivateFirstBinding(); + } + } + } + } } } From c58e4a51b3764527113b86f913bea257407d0254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Fri, 20 Sep 2019 15:24:33 +0200 Subject: [PATCH 2/2] Add comments. --- src/Avalonia.Base/PriorityBindingEntry.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs index 312018d8ee..7f5415c2d8 100644 --- a/src/Avalonia.Base/PriorityBindingEntry.cs +++ b/src/Avalonia.Base/PriorityBindingEntry.cs @@ -129,6 +129,8 @@ namespace Avalonia } else { + // To avoid allocating closure in the outer scope we need to capture variables + // locally. This allows us to skip most of the allocations when on UI thread. var instance = this; var newValue = value;