From 857bfb5bd2b863825c09fbfb780dc379fba4d345 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 22 Jun 2022 16:00:29 +0200 Subject: [PATCH] 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. --- .../Styling/PropertySetterInstance.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index c4e8f47e67..9028224cc1 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -18,7 +18,7 @@ namespace Avalonia.Styling private readonly DirectPropertyBase? _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(_value) : default); + PublishNext(IsActive ? new BindingValue(_value) : default); + } + + private enum State + { + Inactive, + Active, + Disposed, } } }