Bug 573190 - [Databinding] Values are overwritten with observer detail
Summary: [Databinding] Values are overwritten with observer detail
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.20   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-UI-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-04-27 07:37 EDT by Christoph Laeubrich CLA
Modified: 2021-12-31 08:37 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Christoph Laeubrich CLA 2021-04-27 07:37:36 EDT
The case is a bit hard to describe and reproduce, but  maybe one has still some idea what cause this and how to fix it.

I have a 

WritableValue<Person> info = new WritableValue<>();

this object is updated by a user selection (a list of Persons). I bind the values using SWT in two way:

ISWTObservableValue<String> nameField = WidgetProperties.text(SWT.Modify).observe(nameTextField));

IObservableValue<String> infoName = PojoProperties.value("name", String.class).observeDetail(info);

factory.create(nameField::getValue, infoName::setValue);
factory.create(info::getValue, v -> {
    if(v == null) {
        nameField.setValue("");
    } else {
        nameField.setValue(v.getName());
    }
});

Everything works fine (when I change the info object the textfield gets updated, when I change the text my object gets updated).

But when I change the value of "info" fast enough (e.g. user clicks fast or uses up/down arrows repeatably) things blow-up and values are getting overwritten (e.g. suddenly the second entry in the list gets the same value as the first entry).

I can avoid this by pausing all sideefects, set the info value and then resume in an async callback but this is of course not desirable.
Comment 1 Jens Lideström CLA 2021-12-28 12:18:32 EST
Hello, Christoph

Can you attach a minimal, runnable code example that demonstrates the problem?
Comment 2 Christoph Laeubrich CLA 2021-12-30 01:35:02 EST
Sadly I was not able to reproduce this yet with a minimal example and I have no idea what can causing this beside the example describing the problem. I observed this under Linux+Windows so it seems not to be related to the OS...
Comment 3 Christoph Laeubrich CLA 2021-12-30 02:47:52 EST
I played around a bit to find an example an came to the conclusion that this might be some kind of "race-condition" lets stick with the example:


ISWTObservableValue<String> nameField = WidgetProperties.text(SWT.Modify).observe(nameTextField);
IObservableValue<String> infoName = PojoProperties.value("name", String.class).observeDetail(info);
factory.create(nameField::getValue, infoName::setValue);

Lets say the text changes, then a change event is generate from the 'nameField' property.

Once in a while this will be propagated to the 'info' object through the factory binding, but it seems some things are done in sync and some async so there is a chance of getting hold on a wrong state.
Comment 4 Jens Lideström CLA 2021-12-30 04:48:10 EST
(In reply to Christoph Laeubrich from comment #3)
> I played around a bit to find an example an came to the conclusion that this
> might be some kind of "race-condition"

On your explanation it sounds like it might be a race. The flaky symptoms and the different results on different OS:s suggest that.

Question: In the problematic code, is there multiple threads involved? Do you update observables on a background thread, and use the databinding framework to propagate the data to the SWT main thread?

If only a single thread is involved then everything in the databinding framework should happen synchronously and you should see such flaky results.
Comment 5 Christoph Laeubrich CLA 2021-12-30 05:28:33 EST
(In reply to Jens Lideström from comment #4)
> If only a single thread is involved then everything in the databinding
> framework should happen synchronously and you should see such flaky results.

That is not completely true. While in a single-threaded case (what is the case here) there is no concurrency there still race conditions can happen if a shared mutable state is accessed.

In this particular case, take a look at

org.eclipse.core.internal.databinding.observable.sideeffect.SideEffect.scheduleUpdate()

this uses realm.asyncExec what describes as

> Causes the <code>run()</code> method of the runnable
> to be invoked from within this realm at the next
> reasonable opportunity.

That mean it is run synchronous in the realm but with no control of the time, and it explicitly states:

> The caller of this method continues to run in parallel

that means that even calling it from inside the realm does not make it happen instantly.

So it heavily depends on the order and how the update is performed.

e.g. SideEffects are executed after all (or some) other events are executed, depending on the factory used before/after other SideEffects, while e.g using a ValueChangedListener runs at the same time where the change occurs (and thus might modify state seen by a SideEffects later on).

For this specific case I should be able to provide an example if desired, I'm just not sure how/if this could be fixed at all (maybe a BindingContext would be required here instead of SideEffects but its a bit hard to put such restrictions on the consumers of a class to using either of those techniques...

So for now it seems that SideEffects do not play well with WidgetProperties/PojoProperties/...
Comment 6 Jens Lideström CLA 2021-12-30 06:09:44 EST
(In reply to Christoph Laeubrich from comment #5)

Ah, you're right, I understand what you mean. When jobs are posted async and goes into a work queue in an undefined order it becomes really hard to predict exactly what is going to happen.

Should we close this ticket? Since the problem is probably a tricky and weird interaction between SideEffect and async operations. It is not really a bug, and there is no concrete fix.
Comment 7 Christoph Laeubrich CLA 2021-12-30 08:34:38 EST
(In reply to Jens Lideström from comment #6)
> Should we close this ticket? Since the problem is probably a tricky and
> weird interaction between SideEffect and async operations. It is not really
> a bug, and there is no concrete fix.

From users perspective this is clearly a bug if the framework behaves unpredictable.
Comment 8 Jens Lideström CLA 2021-12-30 10:45:36 EST
Just out of curiosity, Christoph, it would be interesting to know a little about how you're using the framework!

Do you rely on the SideEffect system a lot? Do you use it instead of DataBindingContext?

I get that impression because you also commented about SideEffect on bug 565160.
Comment 9 Christoph Laeubrich CLA 2021-12-30 11:40:52 EST
(In reply to Jens Lideström from comment #8)
> Just out of curiosity, Christoph, it would be interesting to know a little
> about how you're using the framework!
> 
> Do you rely on the SideEffect system a lot? Do you use it instead of
> DataBindingContext?
> 
> I get that impression because you also commented about SideEffect on bug
> 565160.

I almost always use SideEffect and never DataBindingContext as it become quite cumbersome to setup everything manually. Also SideEffect has the advantage that I don't need to expose IObservable in the API but they are still recognized...

That way I can simply do

factory.create(()-> {
  //access some generic object to update the UI state
});

so if the objects internally use IObvervables they got tracked and updated, if not then there are just no automatic updates.

I also wrote some helper methods to access data in way so it is possible for implementations to be called from whatever thread, and some that work around several limitations (e.g. clearing a list that is already empty or replacing just changed values in a remove/addAll call).

This allows to have a very flexible and automatic updating UI without much boilerplate code.
Comment 10 Jens Lideström CLA 2021-12-31 07:47:10 EST
(In reply to Christoph Laeubrich from comment #9)
> I almost always use SideEffect and never DataBindingContext

Interesting approach!

Do you have any public code with this pattern that I could look at?
Comment 11 Christoph Laeubrich CLA 2021-12-31 08:37:46 EST
(In reply to Jens Lideström from comment #10)
> (In reply to Christoph Laeubrich from comment #9)
> > I almost always use SideEffect and never DataBindingContext
> 
> Interesting approach!
> 
> Do you have any public code with this pattern that I could look at?

The code is not in a public repository, is there anything you are particularly interested in? I might be able to still share some of the codes or give some insights, feel free to contact me directly via mail if you like or I can also post some examples here if its of broader interest.