Thank you, Christopher, for clarification!

Personally, I would consider this to be a problem with the application design: 
the code should limit the number of alerts shown to the user.  Do you really 
want the user to click through hundreds of alerts?

Nevertheless, you are right about the need for the platform to gracefully 
handle the case of too many nested event loops - by throwing an exception with 
a meaningful message, as Martin proposed in 
https://github.com/openjdk/jfx/pull/1741

Cheers,
-andy



From: Christopher Schnick <crschn...@xpipe.io>
Date: Tuesday, March 25, 2025 at 11:52
To: Andy Goryachev <andy.goryac...@oracle.com>
Cc: OpenJFX <openjfx-dev@openjdk.org>
Subject: Re: [External] : Re: JVM crashes on macOS when entering too many 
nested event loops

Hey Andy,

so I think I was able to reproduce this issue for our application.

There are two main factors how this can happen:
- We use an alert-based error reporter, meaning that we have a default uncaught 
exception handler set for all threads which will showAndWait an Alert with the 
exception message
- As I reported yesterday with 
https://mail.openjdk.org/pipermail/openjfx-dev/2025-March/052963.html, there 
are some rare exceptions that can occur in a normal event loop without 
interference of the application, probably because of a small bug in the bounds 
calculation code

If you combine these two factors, you will end up with an infinite loop of the 
showAndWait entering a nested event loop, the event loop throwing an internal 
exception, and the uncaught exception handler starting the same loop with 
another alert. I don't think this is a bad implementation from our side, the 
only thing that we can improve is to maybe check how deep the uncaught 
exception loop is in to prevent this from occurring indefinitely. But I would 
argue this can happen to any application. Here is a sample code, based on the 
reproducer from the OutOfBounds report from yesterday:

import javafx.application.Application;

import javafx.application.Platform;

import javafx.scene.Scene;

import javafx.scene.control.Alert;

import javafx.scene.control.Button;

import javafx.scene.layout.Region;

import javafx.scene.layout.StackPane;

import javafx.scene.layout.VBox;

import javafx.stage.Stage;



import java.io.IOException;

import java.util.Arrays;



public class ParentBoundsBug extends Application {



    @Override

    public void start(Stage stage) throws IOException {

        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {

            throwable.printStackTrace();



            if (Platform.isFxApplicationThread()) {

                var alert = new Alert(Alert.AlertType.ERROR);

                alert.setHeaderText(throwable.getMessage());

                
alert.setContentText(Arrays.toString(throwable.getStackTrace()));

                alert.showAndWait();

            } else {

                // Do some other error handling for non-platform threads

                // Probably just show the alert with a runLater()



                // For this example, there are no exceptions outside the 
platform thread

            }

        });



        // Run delayed as Application::reportException will only be called for 
exceptions

        // after the application has started

        Platform.runLater(() -> {

            Scene scene = new Scene(createContent(), 640, 480);

            stage.setScene(scene);

            stage.show();

            stage.centerOnScreen();

        });

    }



    private Region createContent() {

        var b1 = new Button("Click me!");

        var b2 = new Button("Click me!");

        var vbox = new VBox(b1, b2);

        b1.boundsInParentProperty().addListener((observable, oldValue, 
newValue) -> {

            vbox.setVisible(!vbox.isVisible());

        });

        b2.boundsInParentProperty().addListener((observable, oldValue, 
newValue) -> {

            vbox.setVisible(!vbox.isVisible());

        });

        vbox.boundsInParentProperty().addListener((observable, oldValue, 
newValue) -> {

            vbox.setVisible(!vbox.isVisible());

        });



        var stack = new StackPane(vbox, new StackPane());

        stack.boundsInParentProperty().addListener((observable, oldValue, 
newValue) -> {

            vbox.setVisible(!vbox.isVisible());

        });

        return stack;

    }



    public static void main(String[] args) {

        launch();

    }

}

If the same OutOfBounds exception from the reported I linked happens in the 
bounds calculation, which happens approximately 1/5 runs for me, this 
application will enter new event loops until it crashes. If the OutOfBounds 
doesn't trigger, it will just throw a StackOverflow but won't continue the 
infinite loop of nested event loops. So for the reproducer it is important to 
try a few times until you get the described OutOfBounds.

I attached the stacktrace of how this fails. The initial StackOverflow causes 
infinitely many following exceptions in the nested event loop.

Best
Christopher Schnick
On 25/03/2025 18:28, Andy Goryachev wrote:
Dear Christopher:

Were you able to root cause why your application enters that many nested event 
loops?

I believe a well-behaved application should never experience that, unless there 
is some design flaw or a bug.

-andy


From: Christopher Schnick <crschn...@xpipe.io><mailto:crschn...@xpipe.io>
Date: Monday, March 10, 2025 at 19:45
To: Andy Goryachev <andy.goryac...@oracle.com><mailto:andy.goryac...@oracle.com>
Subject: [External] : Re: JVM crashes on macOS when entering too many nested 
event loops

Our code and some libraries do enter some nested event loops at a few places 
when it makes sense, but we didn't do anything to explicitly provoke this, this 
occurred naturally in our application. So it would be nice if JavaFX could 
somehow guard against this, especially since crashing the JVM is probably the 
worst thing that can happen.

I looked at the documentation, but it seems like the public API at 
Platform::enterNestedEventLoop does not mention this.
From my understanding, the method Platform::canStartNestedEventLoop is 
potentially the right method to indicate to the caller that the limit is close 
by returning false.
And even if something like an exception is thrown when a nested event loop is 
started while it is close to the limit, that would still be much better than a 
direct crash.

Best
Christopher Schnick
On 10/03/2025 18:51, Andy Goryachev wrote:
This looks to me like it might be hitting the (native) thread stack size limit.

c.s.glass.ui.Application::enterNestedEventLoop() even warns about it:


     * An application may enter several nested loops recursively. There's no

     * limit of recursion other than that imposed by the native stack size.


-andy



From: openjfx-dev 
<openjfx-dev-r...@openjdk.org><mailto:openjfx-dev-r...@openjdk.org> on behalf 
of Martin Fox <martinfox...@gmail.com><mailto:martinfox...@gmail.com>
Date: Monday, March 10, 2025 at 10:10
To: Christopher Schnick <crschn...@xpipe.io><mailto:crschn...@xpipe.io>
Cc: OpenJFX <openjfx-dev@openjdk.org><mailto:openjfx-dev@openjdk.org>
Subject: Re: JVM crashes on macOS when entering too many nested event loops
Hi Christopher,

I was able to reproduce this crash. I wrote a small routine that recursively 
calls itself in a runLater block and then enters a nested event loop. The 
program crashes when creating loop 254. I’m not sure where that limit comes 
from so it’s possible that consuming some other system resource could lower it. 
I couldn’t see any good way to determine how many loops are active by looking 
at the crash report since it doesn’t show the entire call stack.
I did a quick trial on Linux and was able to create a lot more loops (over 600) 
but then started seeing erratic behavior and errors coming from the Java VM. 
The behavior was variable unlike on the Mac which always crashes when creating 
loop 254.

Martin

> On Mar 7, 2025, at 6:24 AM, Christopher Schnick 
> <crschn...@xpipe.io><mailto:crschn...@xpipe.io> wrote:
>
> Hello,
>
> I have attached a JVM fatal error log that seemingly was caused by our JavaFX 
> application entering too many nested event loops, which macOS apparently 
> doesn't like.
>
> As far as I know, there is no upper limit defined on how often an event loop 
> can be nested, so I think this is a bug that can occur in rare situations.
>
> Best
> Christopher Schnick<hs_err_pid.txt>

Reply via email to