Browse Source

Prevent re-entrancy in PropertySetterInstance.Dispose.

The call to `_subscription.Dispose()` causes `BindingEntry.Dispose()` to call `_subscription.Dispose()`, but in this case the `BindingEntry._subscription` instance is the `PropertySetterInstance`!

Except now `PropertySetterInstance._subscription` is null, and so `PropertySetterInstance.Dispose` called `ClearValue`, which is obviously wrong.
Steven Kirk 3 years ago
parent
commit
857bfb5bd2
1 changed files with 20 additions and 7 deletions
  1. 20 7
      src/Avalonia.Base/Styling/PropertySetterInstance.cs

+ 20 - 7
src/Avalonia.Base/Styling/PropertySetterInstance.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Styling
         private readonly DirectPropertyBase<T>? _directProperty;
         private readonly T _value;
         private IDisposable? _subscription;
-        private bool _isActive;
+        private State _state;
 
         public PropertySetterInstance(
             IStyleable target,
@@ -40,6 +40,8 @@ namespace Avalonia.Styling
             _value = value;
         }
 
+        private bool IsActive => _state == State.Active;
+
         public void Start(bool hasActivator)
         {
             if (hasActivator)
@@ -70,31 +72,35 @@ namespace Avalonia.Styling
 
         public void Activate()
         {
-            if (!_isActive)
+            if (!IsActive)
             {
-                _isActive = true;
+                _state = State.Active;
                 PublishNext();
             }
         }
 
         public void Deactivate()
         {
-            if (_isActive)
+            if (IsActive)
             {
-                _isActive = false;
+                _state = State.Inactive;
                 PublishNext();
             }
         }
 
         public override void Dispose()
         {
+            if (_state == State.Disposed)
+                return;
+            _state = State.Disposed;
+
             if (_subscription is object)
             {
                 var sub = _subscription;
                 _subscription = null;
                 sub.Dispose();
             }
-            else if (_isActive)
+            else if (IsActive)
             {
                 if (_styledProperty is object)
                 {
@@ -114,7 +120,14 @@ namespace Avalonia.Styling
 
         private void PublishNext()
         {
-            PublishNext(_isActive ? new BindingValue<T>(_value) : default);
+            PublishNext(IsActive ? new BindingValue<T>(_value) : default);
+        }
+
+        private enum State
+        {
+            Inactive,
+            Active,
+            Disposed,
         }
     }
 }