[jfx19] RFR: 8290331 Binding value left null when immediately revalidated in invalidation listener [v2]
John Hendrikx
john.hendrikx at gmail.com
Sat Jul 16 14:03:58 UTC 2022
I think the issue in JDK-8206449 might not be entirely resolved.
Even though `value` is now cleared in `ObjectBinding`, I noticed that
`ExpressionHelper` also maintains the current value when there are
change listeners registered. Unfortunately, it forgets to clear this
when all change listeners are removed but there are still sufficient
invalidation listeners registered for it to need the Generic
ExpressionHelper implementation.
I think an overhaul of ExpressionHelper might be warranted:
- Doesn't clear current value when it should
- The locking done makes a new copy of the listener lists each time if
there are multiple changes to these lists during an event emission (this
can happen easily when many listener stubs have gone out of scope)
- Change event emission should probably not be dependent on the order of
listeners, see https://bugs.openjdk.org/browse/JDK-8290310
- Further optimizations are possible:
- Better remove performance -- although there is no JDK collection
class that has fast remove(T) while allowing duplicates, it isn't hard
to construct a specialized class that has fast `addLast`, fast
`remove(T)` and fast iteration
- Single invalidation listener could just use the helper field
directly (instanceof check can determine if it is an invalidation
listener or a helper instance); this can save quite some memory as the
fast majority of properties/bindings may never have more than 1 listener
- Single change maybe as well (requires current value to be passed
into #fireValueChangedEvent, which is how most other ExpressionHelpers,
except the base ExpressionHelper, are done) -- note that many, if not
all, users of ExpressionHelper often also have a copy of the current
value maintained separately...
- Small listener lists could use a single array (using instanceof to
distinguish between invalidation/change)
I also find that there is a test for ExpressionHelper, testing a ton of
edge cases, but I think this class shouldn't be tested at all. Instead,
a test should be there to test the behavior of classes implementing
Observable and ObservableValue, regardless of whether they use
ExpressionHelper internally. Such a test would be reusable, regardless
of how these classes manage their listeners, or if some decide to not
use `ExpressionHelper`.
A good JUnit test should I think be constructed first, validating the
current behavior based on classes implementing Observable and
ObservableValue; these should be thorough enough to cover most (if not
all) of the edge cases now tested in ExpressionHelperTest.
With any luck, the test can be written in such a way that it can be
parameterized on the type of class implementing ObservableValue,
allowing a single test to test behavior of all *Property classes and all
*Binding classes.
--John
On 15/07/2022 16:09, Nir Lisker wrote:
> On Fri, 15 Jul 2022 07:19:19 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:
>
>>> I introduced a bug with the fluent bindings PR which affects all ObjectBindings.
>>>
>>> This is the code that fails:
>>>
>>> SimpleObjectProperty<Boolean> condition = new SimpleObjectProperty<>(true);
>>> ObservableValue<String> binding = condition.map(Object::toString);
>>>
>>> binding.addListener(o -> { binding.getValue(); });
>>>
>>> condition.set(false);
>>>
>>> assertEquals(false, binding.getValue()); // returns null (!)
>>>
>>> This PR fixes this problem and adds a test case to cover it.
>> John Hendrikx has updated the pull request incrementally with one additional commit since the last revision:
>>
>> Change explanatory comment to block comment
> Alright, I closed [JDK-8206449](https://bugs.openjdk.org/browse/JDK-8206449) as duplicated of [JDK-8274771](https://bugs.openjdk.org/browse/JDK-8274771).
>
> -------------
>
> PR: https://git.openjdk.org/jfx/pull/829
More information about the openjfx-dev
mailing list