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

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.

Related

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

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();

SecondaryLoop in JavaFX, like Swing?

I have a Java Swing app which I'm investigating if it is even possible to port to JavaFX. The app is a development environment and simulator for an internally used scripting language. The interesting thing about it is you can set breakpoints for this scripting language and step through it, like any programmer would expect for a language.
Now because the language in the simulator is interpreted, deep within the execution of the interpreter, when it hits a breakpoint, it can pop back to the gui with a Java Swing SecondaryLoop class. So when the breakpoint is hit, it calls secondaryLoop.enter(). The gui is then active for the user to inspect variables and gui components are active. When the user hits "Continue" in the program, it calls secondaryLoop.exit() to continue execution of the interpreter. It wouldn't really be feasible for the interpreter to unwind it's entire state to go back to the primary loop, and then take up where it left off at exactly the same point. That's why the SecondaryLoop is invaluable in making it work.
Is this possible in JavaFX?
Yes, it's possible. You need to use the enterNestedEventLoop and exitNestedEventLoop methods (they are inside the com.sun.javafx.tk.Toolkit class). See this usage example:
// Make sure to import the FX Toolkit first
import com.sun.javafx.tk.Toolkit;
// This object will be used as a unique identifier to the nested loop (to
// block the execution of the thread until exitNestedEventLoop is called)
final Object loopLock = new Object();
// Simulate a long process thread (DB call, download, etc)
Thread longProcess = new Thread(new Runnable()
{
#Override
public void run()
{
// Sleep for 12 seconds to simulate a long process
try
{
Thread.sleep(12000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
// Setup a result to pass back to the enterNestedLoop() caller
String result = "Result of this long process";
// We are now done. Call exitNestedEventLoop() to unblock
// the enterNestedLoop() caller. This needs to run from
// the FX Thread so use Platform.runLater()
Runnable fxRunner = new Runnable()
{
public void run()
{
try
{
Toolkit.getToolkit().exitNestedEventLoop(loopLock,
result);
} catch (Throwable t)
{
t.printStackTrace();
}
}
};
Platform.runLater(fxRunner);
}
});
// Start that long process from the FX Thread
longProcess.start();
// The next call will block until exitNestedEventLoop is called, however
// the FX Thread will continue processing UI requests
Object result = Toolkit.getToolkit().enterNestedEventLoop(loopLock);
// Next statement will print: "Result of this long process"
System.out.println("Result is: " + result);
Now, before you use this be warned of two important things:
The com.sun.javafx.tk.Toolkit class is not part of the public API, so Oracle reserves the right of removing it without notice. I've been using it just fine from Java 7 to 8u51 so they could stay there forever, change package/names or disappear completely (unlikely).
Nested loops (and Swing's secondary loops) are great for flexibility and small applications but overusing them often comes with a price. Nesting to many loops (huge stack trace) will often cause "strange" behaviour in your applications since initial parts of your code might end up waiting four or five things ahead completely unrelated to them. I've seen FX nested loops causing "empty" exceptions in FX WebEngine executeScript() calls and duplicating keyboard preprocessing (when pairing FX+Swing) among other problems.
That said I would recommend using the javafx.concurrent.Task instead (if it makes sense). Using the Task class will require a bit more effort but I think it's the correct way of doing things and will probably save you lots of maintenance time.
For extra reference about the FX Task class see this great article: http://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm
UPDATE: enterNestedEventLoop and exitNestedEventLoop will be part of the Java 9 public API (Platform class), more info in JDK-8090865
Hope this helps!

long running application (tail like)

I want to write an tail like app. Now this app, scans a file for changes in the background and fires events in case something changed.
I want to run my application until the user requests to exit it by pressing ctrl + c (working by default). I do not want to create a lot of CPU load just by doing an endless while(true) loop like I'm doing ATM:
try {
// thread doing the monitoring
thread.start();
// forcing the programm not to exit
while (System.in.available() == 0) {
Thread.sleep(5000);
}
} catch (final IOException e) {
e.printStackTrace();
}
Does anyone know a more elegant/the right approach for this?
I'm not sure why you are using a thread in the first place, the 'tail' unix script is simply a while(true) loop with no exit condition. It watches the file and prints changes if any is detected. I think by default it pauses 1 second, so basically:
while(true) {
// Code goes here. Watch file, System.out.print if it changes
Thread.sleep(1000);
}
No need for a thread. But to answer the question about the best way to keep your app alive: Simply don't return from the thread's run() method. You don't have to join the thread, the application will stay in the foreground as long as it has one non-daemon running thread.
If you want to read the System.in without busy-waiting, that's very easy to achieve. InputStream has blocking semantics so all you need to to is call one of the read methods and it will block until there is input available. If you are scanning text input, I'd advise BufferedReader.readLine. Also, do that on the main thread, no point in starting another one.

JFrame not working after first instantiation?

As part of a larger application, I am writing a settings class, which collects and stores user-defined settings. This class is a singleton, and is instantiated during application startup.
In order to accept user input, two different GUI frames are insantiated from within ConfigSettings.java, from a public static method, selectSettings(). Both are subclasses of JFrame. Here is the code for the instantiation of the file selection dialog:
private void selectFile() {
SelectFileGUI fileSelector = new SelectFileGUI();
fileSelector.setVisible(true);
synchronized(this) {
try {
wait();
} catch(Exception e) {
e.printStackTrace();
}
}
fileSelector.dispose();
}
This works fine when the application is initially run. However, at a later point the user may alter their selected settings, including selecting a new source file. This is done by calling selectSettings() again.
The issue I'm having is that any subsequent attempt to instantiate and display these GUI components again results in a new JFrame being displayed, but with a grey background, and no buttons or other components shown. While debugging I was also failing to create new instances of SelectFileGUI directly.
What could be causing this sort of behaviour?
I would check to see if the second time you call it you are using the GUI thread or calling from one of your own threads.
At the top of that method you can test for it (The AWT thread is pretty easily identifiable by name) and have it throw an exception so developers know not to call it on the wrong thread--or you can block their thread and do it in a worker thread.
I don't know what is causing this behavior but in your code the following simply cannot possibly be the right way to manage dialogs (more below):
fileSelector.setVisible(true);
synchronized(this) {
try {
wait();
} catch(Exception e) {
e.printStackTrace();
}
}
fileSelector.dispose();
Do you want your dialogs to be modal or not?
If you want them to be modal, then you simply make a blocking call like when you're invoking JColorChooser.showDialog(...) method and your return "value" is your color/file/whatever.
If you want them non-modal, then you use a callback to get your color/file. In the JColorChooser dialog example, you'd call the createDialog(...) method and use the ok/cancel listeners as callbacks.
I suggest you take a look at sun's tutorial, for example the one on color chooser, to see how to correctly display a modal (or non-modal) dialog:
http://java.sun.com/docs/books/tutorial/uiswing/components/colorchooser.html
Once again, that synchronized(this) { try { wait() ... } to manage something as simple as a file selector/dialog frame just cannot be correct.
Agree with BillK: sounds like you're calling it from outside the EDT first time around (so your call to wait() doesn't block the EDT), then from the EDT the second time around. See SwingUtilities.invokeAndWait() and/or Dialog.setModal().
The consensus here is that you are breaking the rules governing the use of the AWT painting thread (the Event Dispatch Thread).
A couple things to note:
If your code attempts to paint your GUI components outside this painting thread, the gray dialog could be the result of a deadlock between the EDT and the thread your application is using to paint.
If you do get into this situation, you will experience the inability to create new dialogs as described.
However, as you mention that you are debugging while experiencing this problem, it might be that you have paused the EDT through your IDE.
Take a look at this tutorial for some guidelines on how use threads in a client application.
To fully appreciate the issue, it would be nice to see some more code - pertinent parts of selectSettings(), for example.

Java while loop and Threads! [duplicate]

This question already has answers here:
How can I abort a running JDBC transaction?
(4 answers)
Closed 5 years ago.
I have a program that continually polls the database for change in value of some field. It runs in the background and currently uses a while(true) and a sleep() method to set the interval. I am wondering if this is a good practice? And, what could be a more efficient way to implement this? The program is meant to run at all times.
Consequently, the only way to stop the program is by issuing a kill on the process ID. The program could be in the middle of a JDBC call. How could I go about terminating it more gracefully? I understand that the best option would be to devise some kind of exit strategy by using a flag that will be periodically checked by the thread. But, I am unable to think of a way/condition of changing the value of this flag. Any ideas?
I am wondering if this is a good practice?
No. It's not good. Sometimes, it's all you've got, but it's not good.
And, what could be a more efficient way to implement this?
How do things get into the database in the first place?
The best change is to fix programs that insert/update the database to make requests which go to the database and to your program. A JMS topic is good for this kind of thing.
The next best change is to add a trigger to the database to enqueue each insert/update event into a queue. The queue could feed a JMS topic (or queue) for processing by your program.
The fall-back plan is your polling loop.
Your polling loop, however, should not trivially do work. It should drop a message into a queue for some other JDBC process to work on. A termination request is another message that can be dropped into the JMS queue. When your program gets the termination message, it absolutely must be finished with the prior JDBC request and can stop gracefully.
Before doing any of this, look at ESB solutions. Sun's JCAPS or TIBCO already have this. An open source ESB like Mulesource or Jitterbit may already have this functionality already built and tested.
This is really too big an issue to answer completely in this format. Do yourself a favour and go buy Java Concurrency in Practice. There is no better resource for concurrency on the Java 5+ platform out there. There are whole chapters devoted to this subject.
On the subject of killing your process during a JDBC call, that should be fine. I believe there are issues with interrupting a JDBC call (in that you can't?) but that's a different issue.
As others have said, the fact that you have to poll is probably indicative of a deeper problem with the design of your system... but sometimes that's the way it goes, so...
If you'd like to handle "killing" the process a little more gracefully, you could install a shutdown hook which is called when you hit Ctrl+C:
volatile boolean stop = false;
Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {
public void run() {
stop = true;
}
});
then periodically check the stop variable.
A more elegant solution is to wait on an event:
boolean stop = false;
final Object event = new Object();
Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") {
public void run() {
synchronized(event) {
stop = true;
event.notifyAll();
}
}
});
// ... and in your polling loop ...
synchronized(event) {
while(!stop) {
// ... do JDBC access ...
try {
// Wait 30 seconds, but break out as soon as the event is fired.
event.wait(30000);
}
catch(InterruptedException e) {
// Log a message and exit. Never ignore interrupted exception.
break;
}
}
}
Or something like that.
Note that a Timer (or similar) would be better in that you could at least reuse it and let it do with all of the details of sleeping, scheduling, exception handling, etc...
There are many reasons your app could die. Don't focus on just the one.
If it's even theoretically possible for your JDBC work to leave things in a half-correct state, then you have a bug you should fix. All of your DB work should be in a transaction. It should go or not go.
This is Java. Move your processing to a second thread. Now you can
Read from stdin in a loop. If someone types "QUIT", set the while flag to false and exit.
Create a AWT or Swing frame with a STOP button.
Pretend you are a Unix daemon and create a server socket. Wait for someone to open the socket and send "QUIT". (This has the added bonus that you can change the sleep to a select with timeout.)
There must be hundreds of variants on this.
Set up a signal handler for SIGTERM that sets a flag telling your loop to exit its next time through.
Regarding the question "The program could be in the middle of a JDBC call. How could I go about terminating it more gracefully?" - see How can I abort a running jdbc transaction?
Note that using a poll with sleep() is rarely the correct solution - implemented improperly, it can end up hogging CPU resources (the JVM thread-scheduler ends up spending inordinate amount of time sleeping and waking up the thread).
I‘ve created a Service class in my current company’s utility library for these kinds of problems:
public class Service implements Runnable {
private boolean shouldStop = false;
public synchronized stop() {
shouldStop = true;
notify();
}
private synchronized shouldStop() {
return shouldStop;
}
public void run() {
setUp();
while (!shouldStop()) {
doStuff();
sleep(60 * 1000);
}
}
private synchronized sleep(long delay) {
try {
wait(delay);
} catch (InterruptedException ie1) {
/* ignore. */
}
}
}
Of course this is far from complete but you should get the gist. This will enable you to simply call the stop() method when you want the program to stop and it will exit cleanly.
If that's your application and you can modify it, you can:
Make it read a file
Read for the value of a flag.
When you want to kill it, you just modify the file and the application will exit gracefully.
Not need to work it that harder that that.
You could make the field a compound value that includes (conceptually) a process-ID and a timestamp. [Better yet, use two or more fields.] Start a thread in the process that owns access to the field, and have it loop, sleeping and updating the timestamp. Then a polling process that is waiting to own access to the field can observe that the timestamp has not updated in some time T (which is much greater than the time of the updating loop's sleep interval) and assume that the previously-owning process has died.
But this is still prone to failure.
In other languages, I always try to use flock() calls to synchronize on a file. Not sure what the Java equivalent is. Get real concurrency if you at all possibly can.
I'm surprised nobody mentioned the interrupt mechanism implemented in Java. It's supposed to be a solution to the problem of stopping a thread. All other solutions have at least one flaw, that's why this mechanism is needed to be implemented in the Java concurrency library.
You can stop a thread by sending it an interrupt() message, but there are others ways that threads get interrupted. When this happens an InterruptedException is thrown. That's why you have to handle it when calling sleep() for example. That's where you can do cleanup and end gracefully, like closing the database connection.
Java9 has another "potential" answer to this: Thread.onSpinWait():
Indicates that the caller is momentarily unable to progress, until the occurrence of one or more actions on the part of other activities. By invoking this method within each iteration of a spin-wait loop construct, the calling thread indicates to the runtime that it is busy-waiting. The runtime may take action to improve the performance of invoking spin-wait loop constructions.
See JEP 285 for more details.
I think you should poll it with timertask instead.
My computer is running a while loop 1075566 times in 10 seconds.
Thats 107557 times in one second.
How often is it truly needed to poll it? A TimerTask runs at its fastest 1000 times in 1 second. You give it a parameter in int (miliseconds) as parameters. If you are content with that - that means you strain your cpu 108 times less with that task.
If you would be happy with polling once each second that is (108 * 1000). 108 000 times less straining. That also mean that you could check 108 000 values with the same cpu strain that you had with your one while loop - beause the you dont assign your cpu to check as often. Remember the cpu has a clock cycle. Mine is 3 600 000 000 hertz (cycles per second).
If your goal is to have it updated for a user - you can run a check each time the user logs in (or manually let him ask for an update) - that would practically not strain the cpu whatsoever.
You can also use thread.sleep(miliseconds); to lower the strain of your polling thread (as it wont be polling as often) you where doing.

Categories

Resources