-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stack overflow in binding system #16746
Comments
Interesting. If you disable CompiledBindings for a test, does it disappear? |
After adding this to my project file, the result is the same.
The Binding System went though a major refactor (#13970), so I assume that the issue was introduced there. The initial The docs say the |
This issue caught my eye, but it did not help to test without the changes. I thought I'd mention it here anyway. Make bindings react to PropertyChanged even if property hasn't changed #16150 |
Thanks for testing. I also end up in an infinite cycle with another 3rd party control and can't wrap my head around. So if it was compiled bindings extension, that would have been a good entry point. I'm also certain it has to do with the bindings refractor somehow. /cc @grokys will try to see if I can mange to get this into Sandbox to narrow it down as this is also bugging me ^^ |
@busitech I can't reproduce this with a simple sample :-( Can you provide a minimal sample for me to test? My ViewModel: public partial class MainViewModel: ObservableObject
{
[ObservableProperty] private string? _name;
} My MainView: <MaskedTextBox Text="{Binding Name}" Mask="???" /> |
Yes, here is my test project. |
Hello @timunie, were you able to reproduce with my sample? |
Another side effect of the binding system changes is that the source property of the Binding on the Text property of the MaskedTextBox is being set to the masked value, even before any user input has been made in the control. At 11.0.13, no changes would be made to the source property until user input occurs. This would be the correct behavior. |
One input necessary to reproduce this issue is to bind the Text property on the MaskedTextBox to an initial value that does not conform to the mask (including null or empty string). |
Guess what? I was able to reproduce it with your sample attached. I added the relevant parts to Sandbox, and the issue was gone. Can't wrap my head around it yet 🤷 |
I am reproducing with the nightly build 11.2.999-cibuild0051406-alpha, and with my sample directly inside the Avalonia project, using the latest commit. I produced a patch today that takes care of the writing back of the value during the initial subscription, and that took care of the cycle. |
This is a very simple approach to preventing the stack overflow, by preventing the write-back of the value to the source during publish. |
👍 could you make a PR of that patch?
|
Today I made a new patch I am more pleased with. It will be the basis of my PR. |
I ran up against this exception as well. I was using a
No exception when:
And also no exception when using OneWayToSource binding mode. |
@EvanHaklar can you try the linked PR as well with your sample? |
Triggering a property change event for unchanged values should not cause any issues. Sounds like the control relies on some specific behavior. To prevent such issues you usually want to have some internal state that makes sure a computed write back isn't causing an infinite loop. |
@Gillibald The control, MaskedTextBox, is an Avalonia supplied / maintained control. As stated in the attached PR, the binding subsystem is sending obsolete change events containing old values, after those values have already been replaced by newer values, and after events have already been sent to the same subscribers with the most current value. This is the root cause of the endless loop. The MaskedTextBox control relies on this specific behavior: the absence of obsolete and out of sequence events being sent. I believe this would be the correct behavior of the binding system, and not a shortcoming of the control. The control was designed to iterate a couple of times recursively at startup when populated with an empty value, but it did not do so infinitely until the binding system went through an overhaul (that project is referenced above). This caused regression in the MaskedTextBox. I think the binding subsystem lacks loop detection, and also lacks protection from sending stale values in the event of recursion during publication. The write back to source seems to be happening more often, which is also unwanted at times. @timunie Without having a perfect understanding of the entire theory of design behind the binding subsystem, our PR takes a stab at these three major areas, in the hopes of having the additional discussion you mentioned with the team about the items we have identified as root issues. |
@busitech looks like we may have a solution. Maybe you can give the PR a try? #17073 (comment) |
@timunie The new PR does fix the stack overflow. We also included changes in our PR which allowed us to revert to the prior behavior of the control, and that change was not included in #17073. I hope to have further discussion with @grokys about the merits of those changes as well. I added a comment with more details here: #16819 (comment) |
* Add failing test for #16746 * Always read the value from the target object. The value must be read from the target object instead of using the value from the event because the value may have changed again between the time the event was raised and now: if that occurs in a two-way binding then we end up with a stack overflow.
* Add failing test for #16746 * Always read the value from the target object. The value must be read from the target object instead of using the value from the event because the value may have changed again between the time the event was raised and now: if that occurs in a two-way binding then we end up with a stack overflow.
Describe the bug
We are observing a stack overflow, caused by a loop in the binding code. Below are two full iterations of the loop that leads to the stack overflow.
To Reproduce
This issue is very easy to recreate. Adding a MaskedTextBox to a page bound to a string that has an initial value that does not conform to the format of the mask (including null or empty string) will immediately cause a stack overflow when the application starts.
Expected behavior
No stack overflow is expected.
Avalonia version
nightly build 11.2.999-cibuild0051406-alpha
last known good version is 11.0.13
OS
macOS
Additional context
The loop is kicked off initially by this statement:
at Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.InpcPropertyAccessor.SubscribeCore()
The text was updated successfully, but these errors were encountered: