On Mar 25, 2025, at 12:16 PM, Andy Goryachev
<andy.goryac...@oracle.com> wrote:
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 inhttps://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
withhttps://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>