KDAB/KDBindings

Wrong property value during dependent property change

jm4R opened this issue · 4 comments

jm4R commented

One failing test case says more than 1000 words:

TEST_CASE("Binding order")
{

    Property x{ 1 };
    Property x10 = makeBoundProperty(x * 10);
    Property x100 = makeBoundProperty(x * 100);

    int called = 0;
    auto check = [&] {
        called++;
        REQUIRE(x10.get() == x.get() * 10);
        REQUIRE(x100.get() == x.get() * 100);
    };
    auto c1 = x10.valueChanged().connect(check);
    auto c2 = x100.valueChanged().connect(check);

    x = 2;
    REQUIRE(called == 2);
}

This test case is minimal reproducible example of real life usage bug.

Hi, thank you for taking the time to report this issue.

Just to be clear, the REQUIRE that fails is this one, right:

REQUIRE(x100.get() == x.get() * 100);

At first glance, it makes sense that this requirement shouldn never fail.
However, when you consider the synchronous nature of the property binding updates, this behavior is actually normal and expected.
The reason for this is that valueChanged is synchronous. It is emitted immediately after the value has been changed.

So when x updates, both x10 and x100 will update as a consequence.
This indeed happens, but both values cannot update at the same time.
One must be updated before the other.
And observing the change of the value that changed first means that you are able to observe the other property still in an invalid state.
This is what happens here, as x10 is updated first, which causes the check function to be called before the binding evaluator has a chance to update x100.

While this may be unintuitive, this is the currently expected behavior.

If you move the two REQUIRE calls out of the check and to the end of your test case, it will pass.

Can you elaborate on how this issue manifested in real life usage so we can discuss what would be the appropriate way of working with this behavior?

jm4R commented

Thank you for clarification the nature of this behavior, I fully understand it. In other words we can say that this fails because updating bound property is a consequence of emitting valueChanged signal by the property that it is bound to.

What I want to say is that this is not unfixable because this would pass if it (bound property update) would be a consequence of something else, for example valueAboutToChange signal which is emitted earlier or introduce new signal used specially for updating dependent properties. Emitting valueChanged could be a second step after updating whole dependent properties tree. I understand that this would require drastic redesign and I can live without it with a kind of workarounds.

Right, emitting valueChanged after all dependent properties have updated would be another reasonable way to design this.

As you said, changing this behavior would be a considerable change to how KDBindindings works, as it's property binding evaluation is itself built on valueChanged.

I think for now we can close this issue, as long as the workaround is reasonable enough.
This would be an interesting change to consider for a hypothetical KDBindings 2.0 though.

Would you mind sharing your current workaround so other users can benefit from it as well?

One possible work around would be to use a BindingEvaluator and only put the REQUIRE statements (or your actual code) to be executed only once the evaluator is finished. That way the evaluator becomes the delimiter for the "atomic unit" of property updates.