Re: RFR: 8274771: Map, FlatMap and OrElse fluent bindings for ObservableValue [v14]

2022-05-27 Thread Kevin Rushforth
On Tue, 22 Mar 2022 07:46:40 GMT, John Hendrikx  wrote:

>> This is an implementation of the proposal in 
>> https://bugs.openjdk.java.net/browse/JDK-8274771 that me and Nir Lisker 
>> (@nlisker) have been working on.  It's a complete implementation including 
>> good test coverage.  
>> 
>> This was based on https://github.com/openjdk/jfx/pull/434 but with a smaller 
>> API footprint.  Compared to the PoC this is lacking public API for 
>> subscriptions, and does not include `orElseGet` or the `conditionOn` 
>> conditional mapping.
>> 
>> **Flexible Mappings**
>> Map the contents of a property any way you like with `map`, or map nested 
>> properties with `flatMap`.
>> 
>> **Lazy**
>> The bindings created are lazy, which means they are always _invalid_ when 
>> not themselves observed. This allows for easier garbage collection (once the 
>> last observer is removed, a chain of bindings will stop observing their 
>> parents) and less listener management when dealing with nested properties.  
>> Furthermore, this allows inclusion of such bindings in classes such as 
>> `Node` without listeners being created when the binding itself is not used 
>> (this would allow for the inclusion of a `treeShowingProperty` in `Node` 
>> without creating excessive listeners, see this fix I did in an earlier PR: 
>> https://github.com/openjdk/jfx/pull/185)
>> 
>> **Null Safe**
>> The `map` and `flatMap` methods are skipped, similar to `java.util.Optional` 
>> when the value they would be mapping is `null`. This makes mapping nested 
>> properties with `flatMap` trivial as the `null` case does not need to be 
>> taken into account in a chain like this: 
>> `node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty)`.
>>   Instead a default can be provided with `orElse`.
>> 
>> Some examples:
>> 
>> void mapProperty() {
>>   // Standard JavaFX:
>>   label.textProperty().bind(Bindings.createStringBinding(() -> 
>> text.getValueSafe().toUpperCase(), text));
>> 
>>   // Fluent: much more compact, no need to handle null
>>   label.textProperty().bind(text.map(String::toUpperCase));
>> }
>> 
>> void calculateCharactersLeft() {
>>   // Standard JavaFX:
>>   
>> label.textProperty().bind(text.length().negate().add(100).asString().concat("
>>  characters left"));
>> 
>>   // Fluent: slightly more compact and more clear (no negate needed)
>>   label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() + 
>> " characters left"));
>> }
>> 
>> void mapNestedValue() {
>>   // Standard JavaFX:
>>   label.textProperty().bind(Bindings.createStringBinding(
>> () -> employee.get() == null ? ""
>> : employee.get().getCompany() == null ? ""
>> : employee.get().getCompany().getName(),
>> employee
>>   ));
>> 
>>   // Standard JavaFX + Optional:
>>   label.textProperty().bind(Bindings.createStringBinding(
>>   () -> Optinal.ofNullable(employee.get())
>>   .map(Employee::getCompany)
>>   .map(Company::getName)
>>   .orElse(""),
>>  employee
>>   ));
>> 
>>   // Fluent: no need to handle nulls everywhere
>>   label.textProperty().bind(
>> employee.map(Employee::getCompany)
>> .map(Company::getName)
>> .orElse("")
>>   );
>> }
>> 
>> void mapNestedProperty() {
>>   // Standard JavaFX:
>>   label.textProperty().bind(
>> Bindings.when(Bindings.selectBoolean(label.sceneProperty(), 
>> "window", "showing"))
>>   .then("Visible")
>>   .otherwise("Not Visible")
>>   );
>> 
>>   // Fluent: type safe
>>   label.textProperty().bind(label.sceneProperty()
>> .flatMap(Scene::windowProperty)
>> .flatMap(Window::showingProperty)
>> .orElse(false)
>> .map(showing -> showing ? "Visible" : "Not Visible")
>>   );
>> }
>> 
>> Note that this is based on ideas in ReactFX and my own experiments in 
>> https://github.com/hjohn/hs.jfx.eventstream.  I've come to the conclusion 
>> that this is much better directly integrated into JavaFX, and I'm hoping 
>> this proof of concept will be able to move such an effort forward.
>
> John Hendrikx has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Fix wording

I reviewed the public API changes, and this looks like a great addition to 
JavaFX bindings. I think there might be time to get this into JavaFX 19, 
presuming that there are no issues with the testing or implementation, so let's 
proceed down that path.

I left one comment on the API docs as well as pointed out the public methods 
that will need an `@since 19` javadoc tag.

Once that is updated you can propagate the javadoc changes to the CSR 
(including the `@since` tags) and move it to "Proposed". I'll formally review 
it later, once the code review is closer to being done.


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v9]

2022-05-27 Thread Kevin Rushforth
On Fri, 27 May 2022 16:47:04 GMT, Jay Bhaskar  wrote:

>> This PR is new implementation of JavaEvent listener memory management.
>> Issue  
>> [JDK-8088420](https://bugs.openjdk.java.net/browse/JDK-8088420?filter=-1)
>> 
>> 1. Calling remove event listener does not free jni global references.
>> 2. When WebView goes out of scope (disposed from app) , its Event Listeners 
>> are not being garbage collected.
>> 
>> Solution:
>> 1.  Detached the jni global reference from JavaEventListener.
>> 2. Create scoped ref counted wrapper class JavaObjectWrapperHandler for jni 
>> global reference.
>> 3. Create unique  JavaObjectWrapperHandler object for each JavaEventListener.
>> 4. EventListenerManager is a singleton class , which stores the 
>> JavaObjectWrapperHandler mapped with JavaEventListener.
>> 5. EventListenerManager also stores the JavaEventListener mapped with 
>> DOMWindow.
>> 6. When Event listener explicitly removed , JavaEventListener is being 
>> forwarded to EventListenerManager to clear the listener.
>> 7. When WebView goes out of scope, EventListenerManager will de-registered 
>> all the event listeners based on the ref counts attached with WebView 
>> DOMWindow.
>
> Jay Bhaskar has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Adingef else block to unregisterListener

Looks good.

-

Marked as reviewed by kcr (Lead).

PR: https://git.openjdk.java.net/jfx/pull/799


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v9]

2022-05-27 Thread Jay Bhaskar
> This PR is new implementation of JavaEvent listener memory management.
> Issue  
> [JDK-8088420](https://bugs.openjdk.java.net/browse/JDK-8088420?filter=-1)
> 
> 1. Calling remove event listener does not free jni global references.
> 2. When WebView goes out of scope (disposed from app) , its Event Listeners 
> are not being garbage collected.
> 
> Solution:
> 1.  Detached the jni global reference from JavaEventListener.
> 2. Create scoped ref counted wrapper class JavaObjectWrapperHandler for jni 
> global reference.
> 3. Create unique  JavaObjectWrapperHandler object for each JavaEventListener.
> 4. EventListenerManager is a singleton class , which stores the 
> JavaObjectWrapperHandler mapped with JavaEventListener.
> 5. EventListenerManager also stores the JavaEventListener mapped with 
> DOMWindow.
> 6. When Event listener explicitly removed , JavaEventListener is being 
> forwarded to EventListenerManager to clear the listener.
> 7. When WebView goes out of scope, EventListenerManager will de-registered 
> all the event listeners based on the ref counts attached with WebView 
> DOMWindow.

Jay Bhaskar has updated the pull request incrementally with one additional 
commit since the last revision:

  Adingef else block to unregisterListener

-

Changes:
  - all: https://git.openjdk.java.net/jfx/pull/799/files
  - new: https://git.openjdk.java.net/jfx/pull/799/files/79f30067..c609ad9c

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jfx=799=08
 - incr: https://webrevs.openjdk.java.net/?repo=jfx=799=07-08

  Stats: 2 lines in 1 file changed: 0 ins; 1 del; 1 mod
  Patch: https://git.openjdk.java.net/jfx/pull/799.diff
  Fetch: git fetch https://git.openjdk.java.net/jfx pull/799/head:pull/799

PR: https://git.openjdk.java.net/jfx/pull/799


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v8]

2022-05-27 Thread Jay Bhaskar
On Fri, 27 May 2022 15:42:09 GMT, Kevin Rushforth  wrote:

>> Jay Bhaskar has updated the pull request incrementally with one additional 
>> commit since the last revision:
>> 
>>   Change for unregisterDomWindow function and code cleanup
>
> modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.cpp
>  line 57:
> 
>> 55:  }
>> 56: 
>> 57:  if (it->second && it->second->use_count() > 1)
> 
> Shouldn't this be `else if`? The previous block calls `erase(it)`, so it 
> isn't valid to dereference `it` after that call.

Applied and  tested

-

PR: https://git.openjdk.java.net/jfx/pull/799


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v8]

2022-05-27 Thread Kevin Rushforth
On Fri, 27 May 2022 12:13:43 GMT, Jay Bhaskar  wrote:

>> This PR is new implementation of JavaEvent listener memory management.
>> Issue  
>> [JDK-8088420](https://bugs.openjdk.java.net/browse/JDK-8088420?filter=-1)
>> 
>> 1. Calling remove event listener does not free jni global references.
>> 2. When WebView goes out of scope (disposed from app) , its Event Listeners 
>> are not being garbage collected.
>> 
>> Solution:
>> 1.  Detached the jni global reference from JavaEventListener.
>> 2. Create scoped ref counted wrapper class JavaObjectWrapperHandler for jni 
>> global reference.
>> 3. Create unique  JavaObjectWrapperHandler object for each JavaEventListener.
>> 4. EventListenerManager is a singleton class , which stores the 
>> JavaObjectWrapperHandler mapped with JavaEventListener.
>> 5. EventListenerManager also stores the JavaEventListener mapped with 
>> DOMWindow.
>> 6. When Event listener explicitly removed , JavaEventListener is being 
>> forwarded to EventListenerManager to clear the listener.
>> 7. When WebView goes out of scope, EventListenerManager will de-registered 
>> all the event listeners based on the ref counts attached with WebView 
>> DOMWindow.
>
> Jay Bhaskar has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Change for unregisterDomWindow function and code cleanup

The changes look good. While doing my final review I noticed one more thing 
that I think should be changed.

modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.cpp
 line 57:

> 55:  }
> 56: 
> 57:  if (it->second && it->second->use_count() > 1)

Shouldn't this be `else if`? The previous block calls `erase(it)`, so it isn't 
valid to dereference `it` after that call.

-

PR: https://git.openjdk.java.net/jfx/pull/799


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v7]

2022-05-27 Thread Jay Bhaskar
On Thu, 26 May 2022 17:00:46 GMT, Kevin Rushforth  wrote:

>> Jay Bhaskar has updated the pull request incrementally with one additional 
>> commit since the last revision:
>> 
>>   Adding space for map include
>
> modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.cpp
>  line 106:
> 
>> 104: else
>> 105: isReferringToOtherListener = true;
>> 106: }
> 
> I think I now understand what this is trying to do, but it doesn't looks like 
> it's implemented correctly nor is it optimal.
> 
> It seems that the `isReferringToOtherListener` flag is intended to be true 
> iff there is any `JavaEventListener` with a ref count > 1 associated with the 
> Window being unregistered. Ignoring any possible bugs in how it is 
> implemented, there are two problems with this approach. First, you want to 
> remove entries associated with a particular listener if _that_ listener isn't 
> used by another window. So a global "is any listener referring to another 
> window" isn't what you want. Second, since this is multimap, it would seem 
> better to remove individual `(key,value)` pairs associated with the specific 
> window being removed rather than calling erase only for those listeners that 
> aren't being shared at all. If this is feasible, then you wouldn't have to 
> even care if that listener is used by a node in another window. I'm not 
> familiar enough with C++ multimap calls to know how easy this would be, but 
> presumably, it shouldn't be too hard.
> 
> Not directly related to this, it might be cleaner to move this logic to 
> `unregisterDOMWindow` instead of having a separate `resetDOMWindow` method, 
> since this is really part of the same conceptual operation.

I have used the suggested method , and tested it works expected. Thanks

-

PR: https://git.openjdk.java.net/jfx/pull/799


Re: RFR: 8088420: JavaFX WebView memory leak via EventListener [v8]

2022-05-27 Thread Jay Bhaskar
> This PR is new implementation of JavaEvent listener memory management.
> Issue  
> [JDK-8088420](https://bugs.openjdk.java.net/browse/JDK-8088420?filter=-1)
> 
> 1. Calling remove event listener does not free jni global references.
> 2. When WebView goes out of scope (disposed from app) , its Event Listeners 
> are not being garbage collected.
> 
> Solution:
> 1.  Detached the jni global reference from JavaEventListener.
> 2. Create scoped ref counted wrapper class JavaObjectWrapperHandler for jni 
> global reference.
> 3. Create unique  JavaObjectWrapperHandler object for each JavaEventListener.
> 4. EventListenerManager is a singleton class , which stores the 
> JavaObjectWrapperHandler mapped with JavaEventListener.
> 5. EventListenerManager also stores the JavaEventListener mapped with 
> DOMWindow.
> 6. When Event listener explicitly removed , JavaEventListener is being 
> forwarded to EventListenerManager to clear the listener.
> 7. When WebView goes out of scope, EventListenerManager will de-registered 
> all the event listeners based on the ref counts attached with WebView 
> DOMWindow.

Jay Bhaskar has updated the pull request incrementally with one additional 
commit since the last revision:

  Change for unregisterDomWindow function and code cleanup

-

Changes:
  - all: https://git.openjdk.java.net/jfx/pull/799/files
  - new: https://git.openjdk.java.net/jfx/pull/799/files/d6fd438b..79f30067

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jfx=799=07
 - incr: https://webrevs.openjdk.java.net/?repo=jfx=799=06-07

  Stats: 74 lines in 5 files changed: 11 ins; 23 del; 40 mod
  Patch: https://git.openjdk.java.net/jfx/pull/799.diff
  Fetch: git fetch https://git.openjdk.java.net/jfx pull/799/head:pull/799

PR: https://git.openjdk.java.net/jfx/pull/799