Platform.exit() has no effect within infinite loop in JavaFX - java

EDIT: I had originally believed this problem to have been caused by the ifPresentOrElse statement, however I now understand this is not the case and the infinite while loop is to blame for this behavior, and have renamed the question (see comments and chosen answer).
There is an existing question here that shares a similar behavior with this question, however I believe that the solutions are different enough for this not to be considered a duplicate.
Original Question:
Suppose I have a JavaFX application whose start method (in the Application thread) contains the following code:
while(true) {
new TextInputDialog().showAndWait()
.ifPresentOrElse(System.out::println,
Platform::exit);
}
The behavior of this should be that, if the OK button of the TextInputDialog is pressed (making a result present), the text entered within the dialog should be printed. If the CANCEL button is pressed, the Platform::exit statement will be called and the JavaFX application will exit.
The former case works as expected, however the latter doesn't. If the CANCEL button is pressed, the application stays alive and the dialog is opened again as if the OK button had been pressed, however no text is printed, which means that the Platform::exit statement must have been reached instead of the System.out::println statement. In an attempt to debug this issue, I adjusted the original code to the following:
while(true) {
new TextInputDialog().showAndWait()
.ifPresentOrElse(System.out::println,
() -> System.out.println("The latter statement has been reached"));
}
When running this code and pressing the CANCEL button, "The latter statement has been reached" is printed to the screen, proving that the Platform::exit was being reached in the original code, but was not closing the application as expected.
Interestingly enough, if I edit the original code once more to the following:
while(true) {
new TextInputDialog().showAndWait()
.ifPresentOrElse(System.out::println,
() -> System.exit(0));
}
...the program exits as expected.
I have never encountered a behavior like this before, and I am truly at a loss as to what is going on. Any insight would be greatly appreciated.

Read the documentation of Platform.exit():
Causes the JavaFX application to terminate. If this method is called after the Application start method is called, then the JavaFX launcher will call the Application stop method and terminate the JavaFX application thread. The launcher thread will then shutdown. If there are no other non-daemon threads that are running, the Java VM will exit. If this method is called from the Preloader or the Application init method, then the Application stop method may not be called.
This method may be called from any thread.
As can be seen, calling the method will not kill threads, so the thread running your while(true) loop will continue to run, and will prevent the program from ending.
You need to change the code so the while loop ends too, e.g. something like this:
AtomicBoolean keepRunning = new AtomicBoolean(true);
while (keepRunning.get()) {
new TextInputDialog().showAndWait()
.ifPresentOrElse(System.out::println,
() -> keepRunning.set(false));
}
Platform.exit();

Related

SecondaryLoop.enter() not blocking until exit() is called on the EDT

Summary
For some reason when I call SecondaryLoop.enter() on the AWT Event Dispatch Thread (EDT), it does not wait for SecondaryLoop.exit() to be called before unblocking.
Background
Since I think SecondaryLoop is not a very well-known class, I'll give a brief overview:
In general, it is a bad idea to have any long-executing or blocking code running on the EDT because then your app will not be responsive to any events until that code terminates. The EventQueue.createSecondaryLoop() allows you to create a new event loop that will handle events, allowing you to block the EDT without loss of responsiveness. This is what swing modal dialogs use to allow you to block your EDT while you wait for the dialog to be closed, but still allow controls on the dialog itself to be able to operate.
After creating your SecondaryLoop instance, you should be able to call enter() and it should block until exit() is called.
From the docs
This method can be called by any thread including the event dispatch thread. This thread will be blocked until the exit() method is called or the loop is terminated. A new secondary loop will be created on the event dispatch thread for dispatching events in either case.
I'm not entirely sure what it means when it says "or the loop is terminated" though. That could be my issue.
Test Code
The calling the enter() method on a thread other than EDT, blocks as I would expect:
System.out.println("Enter Loop");
Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter();
System.out.println("Done (we should never get here)");
Output:
Enter Loop
However, if we call it on the EDT, it blocks for about a second, but then continues on:
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
Output:
Enter Loop
Done (we should never get here)
Per the comment by tevemadar (thanks BTW), I have updated the code to prevent any sort of possible garbage collection issue:
//Storing loop in array as a quick hack to get past the
// "final or effectively final" issue when using this in the invokeAndWait
SecondaryLoop loop[] = new SecondaryLoop[1];
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> {
loop[0] = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
loop[0].enter();
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
//Just printing this to make sure that it is used after the invokeAndWait is done. This is just
//to make sure there isn't some sort of optimization thing that is deciding that we don't
//need this anymore and allowing the loop to be garbage collected
System.out.println(loop[0]);
Output:
Enter Loop
Done (we should never get here)
java.awt.WaitDispatchSupport#2401f4c3
So, while it was a good suggestion, that does not appear to be my issue.
This seems pretty contradictory to the documentation (and the whole purpose of SecondaryLoop to me. Am I missing something?
Environment
OS: Windows 10
Java:
C:\Program Files\Java\jre8\bin>java.exe -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
Update
On a hunch, I tried adding a timer that continually adds more events to the EDT loop. It seems that adding the timer keeps the loop alive and makes it blocking:
//Add a keep alive timer which adds an event to the EDT for every 0.5 sec
new Timer(500, null).start();
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
With that code, it hangs as I expect, and if I put in some code that calls the exit() method on the loop after some time, it terminates as I would expect. So it seems that the loop must terminate itself once it has gone a certain amount of time without an event (but only if it was originally triggered from the EDT for some reason...).
I suppose I can add timers that do nothing whenever I need to use this feature, but that is definitely more of a work-around hack than a fix in my opinion.
Figured it out (at least this specific problem, I still have some more related issues, but I'm hoping I can figure them out on my own).
I decided to start debugging around in the java source code and I realized that my thread was getting unblocked due to this segment in java.awt.EventQueue:
/**
* Called from dispatchEvent() under a correct AccessControlContext
*/
private void dispatchEventImpl(final AWTEvent event, final Object src) {
event.isPosted = true;
if (event instanceof ActiveEvent) {
// This could become the sole method of dispatching in time.
setCurrentEventAndMostRecentTimeImpl(event);
((ActiveEvent)event).dispatch();
} else if (src instanceof Component) {
((Component)src).dispatchEvent(event);
event.dispatched();
} else if (src instanceof MenuComponent) {
((MenuComponent)src).dispatchEvent(event);
} else if (src instanceof TrayIcon) {
((TrayIcon)src).dispatchEvent(event);
} else if (src instanceof AWTAutoShutdown) {
if (noEvents()) {
dispatchThread.stopDispatching();
}
} else {
if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
getEventLog().fine("Unable to dispatch event: " + event);
}
}
}
In my case src was AWTAutoShutdown, which resulted in my secondary loop to terminate before I called exit().
I found this article which explains that in order to make sure the event queue eventually terminates when all windows are disposed, awt determines when all components are no longer displayable and the event queue is empty, then waits for 1 second, then triggers the event with the AWTAutoShutdown class as the source which terminates the event queue and allows the JVM to terminate. That 1 second timeout is why I observed that it would hang for a little bit.
This explains why adding a timer made my code work (since I was adding an event every half second and the timeout for AWTAutoShutdown is 1 second, the event queue would be kept alive).
The use case for all of this is to basically create an EDT-safe semaphore that will allow events to keep being executed even when it's waited for on the EDT (which I use to display JavaFX dialogs from a Swing application and make it behave like a native swing modal dialog). So in my actual use case, this should work just fine (because there should always be some swing component displaying in my actual applications). However, I hadn't even actually tried out my actual use case. Being a big believer in TDD, I first focused on my JUnit tests, which didn't actually create any UI components.
So, I did a quick test with a little dummy app that does have a GUI, it works just fine. I'm just going to add my 500 ms timer into my unit tests and start it and stop it before each test.
After doing that, I'm still running into some issues with some of my tests, but my minimal verifiable example from my original question works just fine. I'll dig into the remaining test failures and hopefully figure them out on my own. If it seems to be a related issue, then I'll just add a new SO question and put a link to it here.

Need help making a delay in a javafx application

I am making a javafx application that simulates a robot vacuum.
I want it to be automated so it would vacuum the environment by itself.
I need to insert a delay so a human can see the steps the vacuum is taking as it traverses the environment.
So far all the delay methods I have tested crash my program if they are inside a while loop.
If I put it outside the while and just click a button for the next step, everything works fine.
It also works fine if I set the delay to really short time, like 1 ms.
Any ideas of why this is happening?
Any application that executes a set of instructions for a while (is busy) and cannot respond to user input or system events is "seen" by Windows as "not responding" and when you try to interact with a "not responding" program, Windows will tell you it crashed.
The problem, you see, is that you try to delay interface updates with a while loop, and that makes your program execute something for a while and while is busy executing your loop it cannot respond to system or user events.
If you want to make delayed updates, use multithreading. Your while loop is blocking the main thread which is also responsible for rendering and taking any input, so you cannot block this thread. Create another thread and share state (eg. use observer pattern). And then you can execute TimeUnit's sleep() in this helper thread and it won't make your app "crash".

Execution of a program

I have a question about execution of program. In C++ for example, the program is being executed line by line until it reaches the end (return 0; or just reaches the end of the scope of the main function). In a program with a text interface if I want to repeat the program until a user for instance hits the [Esc] key I put it in the loop that is being executed until that specific one situation occurs. Same happens in a GUI program (precisely: WinAPI), program is looping through the event loop until for example user hits 'X' and then the program is reaching the end of the main function, so the execution of the program stops. So far so good.
What is surprising me and is keeping me up at night is the fact that when I create a JFrame object, initialize it and make it happen just to show it, when the program execution reaches the end of the scope of the main function, the window still exists. I've also checked it in a debugger that it's not showing a line that is actually being executed. It clearly states that the thread execution has been finished. I am sure that the execution is not being frozen so something has to be happening under the hood, cause I still can resize, close the window and so on. So here's my question: where is actually the execution of a program 'moving to' when a window in Java is being displayed?
Same thing is happening in VBA. I've created a main function in a module, then in that one module i've forced one non-modal form to show and once the execution of the function has finished, the window did not disappear, but the variables declared in the function did, so the access to them vanished as well.
In both examples in C/C++ those situations would mean, that program was finished, but there's that window visible, so the application actually didn't stop, right?

Exiting one thread closes all

I have a java app which has a menu. One of the menu items is Exit. Which is defined as follows:
item_exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
Another menu item is New, which makes another instance (?) of the same program run in parallel. It is defined as follows:
item_new.addActionListener(new ActionListener() {
MyApp app = new MyApp();
app.start();
}
});
It works as desired except for one problem. It's that when I close one of them, both of them close. The entire app is built on one JFrame object. I don't think changing the default close operation of it will help. I think the issue is with system.exit(0). But, what is the alternative to fix this? I only want the thread I closed to close, not all of them. Thanks.
Creating an object and callings its start() method doesn't make another program run in parallel. It only creates an object, in the same JVM, and executes its start() method, in the same JVM.
System.exit() exits the JVM, so everything running in this JVM stops running.
To make a JFrame invisible, you call setVisible(false) on it. That won't stop the JVM.
System.exit(0); makes the entire Java machine to quit, with everything that is running within. Try not to use this, unless you really need this to happen.
For quitting, perhaps try checking this How to close a Java Swing application from the code
System.exit(0) quits the complete program, not only the current thread. If you want to quit a thread, you have two options: a thread automatically quits as soon as the corresponding Runnables run() method finishes, or you can kill the thread using thread.stop() (just for completeness, shouldn't be used).
Please read the documentation for System.exit(). It specifically states:
Terminates the currently running Java Virtual Machine.
This means the JVM will terminate, and all of your threads with it.
System.exit(0) shuts the whole JVM down.
Your two instances run on the same JVM : you don't fork a process, you just create another instance of your MyApp class. So it is obvious that both "applications" will be killed.
Instead of calling System.exit(0), you should send a termination signal to the JFrame you want to close
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
This way, only that JFrame (or "application") will be killed.
Another option would be to fork a whole new process. For that, I recommend you take a look at the Process class.
Basically, you'll need to change the action of "Exit" so that it's able to detect when there's another "app" currently running ... and to do something plausible in that case. Maybe you alert the user that the other activity hasn't finished yet, and warn him that if he really wants to exit now, the other activity will be abandoned. Maybe you launch the other activity in such a way that it is altogether separate and therefore doesn't care. There are several good alternatives, so this really becomes a design decision on your part: what strategy makes the most intuitive sense for you, and for your users?

Java program only works with Breakpoints in Netbeans

I'm working on a multithreaded program in Java that uses a shared array to pass data between threads. It's being developed in Netbeans 6.7.1.
One of the threads only seems to work when a breakpoint is placed in it, it doesnt matter where it is.
Running in debug mode with no breakpoints acts the same as running in release - the expected output never arrives.
I can't tell where the problem occurs, as the moment a breakpoint is added and I press continue, it works as expected.
How can I narrow down where/why this problem occurs?
Example code:
result = utils.isBufferFull(AudioDuplex.voiceArray);
if(result == true) {
System.out.println("Taking copy");
voiceArray = AudioDuplex.voiceArray;//.clone();
utils.clearBuffer(AudioDuplex.voiceArray);
}
If a breakpoint is placed on line 2, it is never hit.
A breakpoint on line 3 will be hit, and the expected output will arrive.
It's impossible to tell exactly what's wrong without a lengthier code sample, but in my experience, this kind of behavior is typical of unrecognized producer-consumer problems (see http://en.wikipedia.org/wiki/Producer-consumer_problem).
Basically, what's probably happening is that your producer thread does not have the data available when the consumer thread is requesting it. The basic solution is to keep a semaphore (there is a Sempahore class in java afaik). The producer would post when it has the data, the consumer would wait until the producer posts.
What you're seeing with the break point is you stopping the consumer thread for a long enough period that the producer can offer something. When you don't break, the consumer runs normally, and exits before the producer has anything.
Write the values of the involved variables to a log file, the console or add them to an array and print them as soon as you get the error.
Your problem is probably a runtime issue (a second thread updates an involved variable). Since breakpoints only stop the active thread, the second thread gets its work done so the code works.

Categories

Resources