From 1ad7f27cf9a559ac4b51aa6d7cd8ffb6a769c5cb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 3 Jul 2018 17:01:51 -0500 Subject: [PATCH] Make DeferredSetter tied to AvaloniaProperty and use a regular Dictionary. Remove SettingStatus entries for bindings from the dictionary for bindings that don't create a queue of pending values. --- src/Avalonia.Base/IPriorityValueOwner.cs | 2 +- src/Avalonia.Base/Utilities/DeferredSetter.cs | 58 ++++++++++++++----- src/Avalonia.Base/Utilities/SingleOrQueue.cs | 2 +- src/Avalonia.Base/ValueStore.cs | 6 +- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/Avalonia.Base/IPriorityValueOwner.cs b/src/Avalonia.Base/IPriorityValueOwner.cs index 8eaefac4f2..8cbf212381 100644 --- a/src/Avalonia.Base/IPriorityValueOwner.cs +++ b/src/Avalonia.Base/IPriorityValueOwner.cs @@ -33,6 +33,6 @@ namespace Avalonia /// void VerifyAccess(); - DeferredSetter Setter { get; } + DeferredSetter Setter { get; } } } diff --git a/src/Avalonia.Base/Utilities/DeferredSetter.cs b/src/Avalonia.Base/Utilities/DeferredSetter.cs index 9e3cf023c7..15c85338c7 100644 --- a/src/Avalonia.Base/Utilities/DeferredSetter.cs +++ b/src/Avalonia.Base/Utilities/DeferredSetter.cs @@ -10,10 +10,8 @@ namespace Avalonia.Utilities /// A utility class to enable deferring assignment until after property-changed notifications are sent. /// Used to fix #855. /// - /// The type of the object that represents the property. /// The type of value with which to track the delayed assignment. - class DeferredSetter - where TProperty: class + class DeferredSetter { private struct NotifyDisposable : IDisposable { @@ -47,20 +45,37 @@ namespace Avalonia.Utilities return pendingValues ?? (pendingValues = new SingleOrQueue()); } } + + public bool IsSimpleSet => pendingValues?.HasTail != true; } - private readonly ConditionalWeakTable setRecords = new ConditionalWeakTable(); + private Dictionary _setRecords; + private Dictionary SetRecords + => _setRecords ?? (_setRecords = new Dictionary()); + + private SettingStatus GetOrCreateStatus(AvaloniaProperty property) + { + if (!SetRecords.TryGetValue(property, out var status)) + { + status = new SettingStatus(); + SetRecords.Add(property, status); + } + + return status; + } /// /// Mark the property as currently notifying. /// /// The property to mark as notifying. /// Returns a disposable that when disposed, marks the property as done notifying. - private NotifyDisposable MarkNotifying(TProperty property) + private NotifyDisposable MarkNotifying(AvaloniaProperty property) { Contract.Requires(!IsNotifying(property)); - - return new NotifyDisposable(setRecords.GetOrCreateValue(property)); + + SettingStatus status = GetOrCreateStatus(property); + + return new NotifyDisposable(status); } /// @@ -68,19 +83,19 @@ namespace Avalonia.Utilities /// /// The property. /// If the property is currently notifying listeners. - private bool IsNotifying(TProperty property) - => setRecords.TryGetValue(property, out var value) && value.Notifying; + private bool IsNotifying(AvaloniaProperty property) + => SetRecords.TryGetValue(property, out var value) && value.Notifying; /// /// Add a pending assignment for the property. /// /// The property. /// The value to assign. - private void AddPendingSet(TProperty property, TSetRecord value) + private void AddPendingSet(AvaloniaProperty property, TSetRecord value) { Contract.Requires(IsNotifying(property)); - setRecords.GetOrCreateValue(property).PendingValues.Enqueue(value); + GetOrCreateStatus(property).PendingValues.Enqueue(value); } /// @@ -88,9 +103,9 @@ namespace Avalonia.Utilities /// /// The property to check. /// If the property has any pending assignments. - private bool HasPendingSet(TProperty property) + private bool HasPendingSet(AvaloniaProperty property) { - return setRecords.TryGetValue(property, out var status) && !status.PendingValues.Empty; + return SetRecords.TryGetValue(property, out var status) && !status.PendingValues.Empty; } /// @@ -98,9 +113,17 @@ namespace Avalonia.Utilities /// /// The property to check. /// The first pending assignment for the property. - private TSetRecord GetFirstPendingSet(TProperty property) + private TSetRecord GetFirstPendingSet(AvaloniaProperty property) + { + return GetOrCreateStatus(property).PendingValues.Dequeue(); + } + + private void CleanupSetStatus(AvaloniaProperty property) { - return setRecords.GetOrCreateValue(property).PendingValues.Dequeue(); + if (SetRecords.TryGetValue(property, out var status) && status.IsSimpleSet) + { + SetRecords.Remove(property); + } } public delegate bool SetterDelegate(TSetRecord record, ref TValue backing, Action notifyCallback); @@ -116,7 +139,7 @@ namespace Avalonia.Utilities /// /// The value to try to set. public bool SetAndNotify( - TProperty property, + AvaloniaProperty property, ref TValue backing, SetterDelegate setterCallback, TSetRecord value) @@ -145,6 +168,9 @@ namespace Avalonia.Utilities } }); } + + CleanupSetStatus(property); + return updated; } else if(!object.Equals(value, backing)) diff --git a/src/Avalonia.Base/Utilities/SingleOrQueue.cs b/src/Avalonia.Base/Utilities/SingleOrQueue.cs index f33adaff37..37eada3d96 100644 --- a/src/Avalonia.Base/Utilities/SingleOrQueue.cs +++ b/src/Avalonia.Base/Utilities/SingleOrQueue.cs @@ -11,7 +11,7 @@ namespace Avalonia.Utilities private Queue Tail => _tail ?? (_tail = new Queue()); - private bool HasTail => _tail != null; + public bool HasTail => _tail != null; public bool Empty { get; private set; } = true; diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index 0e404500a1..c8e2124182 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/src/Avalonia.Base/ValueStore.cs @@ -168,14 +168,14 @@ namespace Avalonia return value; } - private DeferredSetter _defferedSetter; + private DeferredSetter _defferedSetter; - public DeferredSetter Setter + public DeferredSetter Setter { get { return _defferedSetter ?? - (_defferedSetter = new DeferredSetter()); + (_defferedSetter = new DeferredSetter()); } } }