Skip to content
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

Consider update and accumulate methods on Property #60

Open
kaifox opened this issue Feb 9, 2021 · 15 comments
Open

Consider update and accumulate methods on Property #60

kaifox opened this issue Feb 9, 2021 · 15 comments

Comments

@kaifox
Copy link
Member

kaifox commented Feb 9, 2021

In addition to pure set() methods, a property could have update-like methods like an Atomic reference which should then ensure atomic updates of the value.

Proposed signatures:

Property<T>{
  update(UnaryOperator<T> updateOp);
  accumulate(T in, BinaryOperator<T> updateOp);

 // potentially also these variants (or only those)
  updateAndGet(UnaryOperator<T> updateOp);
  accumulateAndGet(T in, BinaryOperator<T> updateOp);
}

In principle, e.g. update would do the following in an atomic way:

T value = get();
set(updateOp.apply(value));
@andreacalia
Copy link
Member

Ciao @kaifox ! Then it'll really become a "listenable" AtomicReference 😄

@michi42
Copy link
Member

michi42 commented Feb 10, 2021

Sounds fine to me :-)

We would probably have to extend DispatchingObservableValue a bit to delegate these operations to the internal AtomicReference it holds (we should not introduce another one in Property I think). But since this is our own internal class - I don't see any problem with that.

Of course, the notification of subscribers will be non atomic, dispatched onto another thread.
The only guarantee will be that a subsequent get() will give the new value.

Edit: Another thing we have to be aware of - all our transformations are also async subscribers. So if you do

Property<T> property = ...;
ObservableValue<T> derived = property.map(...);
property.update(...);
derived.get();

it is not guaranteed that derived.get() will give you the new value.

@kaifox
Copy link
Member Author

kaifox commented Feb 10, 2021

Yes indeed, then it is a listenable atomic reference ;-) ... Indeed we should not have a second atomic reference ... Yes and the listener notification and the derived will not be atomic... but I think it still is worth it ...

I use such constructs a lot e.g. when you want to publish a state of e.g. a service and one has to do consistent modifications (transformations) of it. And in principle you want to make sure that you do not mix inputs, but 'chain' them. So at the moment one either has to synchronize or enforce a single thread ... both some effort if you use the pattern more frequently ...

With an 'ObservableAtomicReference' it would be simpler ;-) ...

@michi42
Copy link
Member

michi42 commented Feb 10, 2021

Yes, I agree, actually I always thought of Property as an "AtomicReference plus". So if you want to give it a try, please do.

@kaifox
Copy link
Member Author

kaifox commented Feb 10, 2021

I will try (soon) ;-)

@kaifox
Copy link
Member Author

kaifox commented Mar 12, 2021

Gi guys, I finally started trying this ... However, soon found a bit a showstopper:
With all these operations (accumulate, update, ... ) one either can get the old value OR the new value, but it is not possible to get both in a threadsafe consistent way. For the dispatching 'on change' we would need both ....

Currently, I have not idea how to solve this cleanly... Any proposals?

@michi42
Copy link
Member

michi42 commented Mar 12, 2021

Perhaps we could use getAndUpdate() to fetch the previous value, and within the actual update callback we save the new value before we return?

@kaifox
Copy link
Member Author

kaifox commented Mar 15, 2021

@michi42, do you mean more or less something like this?:

public T getAndUpdate(UnaryOperator<T> operator) {
  AtomicReference<T> newValue = new AtomicReference<>();
  T oldValue = lastValue.getAndUpdate(x -> {
     T v = operator.apply(x);
     newValue.set(v);
     return v;
  })
  dispatchChange(oldValue, newValue.get());
  return oldValue;
}

... It all looks a bit clumsy and I am completely unsure if it is bulletproof .... I am not sure, if e.g. the update operations might be retried etc...

@andreacalia
Copy link
Member

Hi guys,

I didn't try it unfortunately.. but if void DispatchingObservableValue.dispatchValue(T) becomes something like T DispatchingObservableValue.updateAndDispatchValue(UnaryOperator<T>)? In the line of Michi's comment I think dispatch should happens atomically 🤔

@michi42
Copy link
Member

michi42 commented Mar 17, 2021

Hi,

From what I see, the function can be applied multiple times, but it is always the last result that will be propagated - so the hack you do should be safe as far as I can see (in the current implementation of AtomicReference that is...)
It is a bit a pity that AtomicReference has no getUpdateAndGet() method, since all the information is there in the implementations ...

@kaifox
Copy link
Member Author

kaifox commented Mar 17, 2021

Ok ... I had similar thoughts ... so if you consider it 'ok' I will try to make a proposal based on this...

@kaifox
Copy link
Member Author

kaifox commented Mar 17, 2021

@andreacalia , the problem is that - for the update on change notification - one needs both, old and final value ...

andreacalia added a commit that referenced this issue Mar 18, 2021
…nal. Waiting for #60 implementation to take this decision.
@andreacalia
Copy link
Member

true indeed ! I see it now the problem.
The solution is a bit convoluted but should work as mentioned by michi 😉 .. anyway I don't see other solutions without introducing many more changes 😇
@kaifox if you have a branch locally rebase first because there's an update that could affect you 🙂

andreacalia added a commit that referenced this issue Mar 18, 2021
…nal. Waiting for #60 implementation to take this decision.
kaifox pushed a commit that referenced this issue Mar 18, 2021
…nal. Waiting for #60 implementation to take this decision.
@kaifox
Copy link
Member Author

kaifox commented Mar 18, 2021

There is a PR out #62 ... but I am not sure ... did I mess up something with the rebase? damn ...

@andreacalia
Copy link
Member

Ciao Kajetan, I think it's ok just rebase on top of master now and it should be perfectly in sync 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants