Search code examples
javaswingconcurrencyedt

Is it OK to call Future.get() on a non-EDT thread from the EDT thread?


This is in the context of unit testing (as it happens).

At the end of a test, whatever the result, I want the code to check for the presence (visibility) of a JFileChooser dialog... and if visible, dismiss it.

There are different ways of dismissing a dialog, of course, but to mimic human action (and to give an example of the sort of question I'm concerned with here) I choose to use java.awt.Robot. The latter's methods should be run in a non-EDT thread.

In fact I have extended Robot to include a convenience method called type_input, so

robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT )

presses first Alt, then F4, then releases F4 and then Alt: like a human dismissing a window/dialog.

I submit the Runnable using invokeAndWait because I don't want the code to run on to the next test until this dialog has been dismissed. I have to test for visibility and focus in the EDT. But the Robot method must, as I said, be run in a non-EDT.

Are there any potential problems with going get() like this in the EDT? Would it potentially lead to unresponsiveness of the GUI? The thing is, I have heard that the framework is capable of "starting a new EDT pump" under certain conditions. I have to confess this is one of the aspects of EDT-related matters that I feel I know least about...

import java.awt.EventQueue;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

class MainFrame extends JFrame {
    JFileChooser update_target_file_chooser;
    JDialog file_chooser_dlg;

    // ... rest of class

}

public class ThreadWithinThread {

    public static void main(String[] args) throws InvocationTargetException, InterruptedException {

        final ExecutorService thread_pool_exec_serv = Executors.newFixedThreadPool( 5 );

        class DismissDlg implements Runnable {
            MainFrame main_frame;
            Robot robot;

            @Override
            public void run() {
                boolean focus_on_dlg = main_frame.file_chooser_dlg.hasFocus();
                if( main_frame.file_chooser_dlg.isVisible() ){
                    if( ! focus_on_dlg ){
                        main_frame.file_chooser_dlg.requestFocus();
                    }
                    class AltF4 implements Callable<Void>{
                        public Void call(){
                            return robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT );
                        }
                    }
                    Future<Void> future_result = thread_pool_exec_serv.submit( new AltF4() );
                    try {
                        // this is the line I'm worried about
                        future_result.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        EventQueue.invokeAndWait( new DismissDlg() );
    }

}

later

As I have said in my responses, this is not about a particular practical solution to the case in hand: I'm really trying to understand whether and when another EDT "event pump" starts up with Future.get() blocking the EDT. As I say I find this whole aspect difficult to understand. My use of JOptionPane's static methods (which must be run in the EDT, see posts on SO ad nauseam) demonstrates to me that these methods (which do block the EDT!) don't actually seem to prevent the GUI from functioning (NB not to be confused with whether these JOptionPanes, or rather their JDialogs, are modal or modeless).

My understanding is that this is due to the fact that "the framework" then starts another "event pump". So could it do so here?


Solution

  • So, from the JavaDocs

    Waits if necessary for the computation to complete, and then retrieves its result.

    Which suggests that the method is a blocking method, if there's one thing you shouldn't do from within the context of the Event Dispatching Thread, it's call a blocking method within the context of the Event Dispatching Thread

    Ah, but what to do? You could use a SwingWorker instead, which internally uses it's own ExecutorService, or submit the worker to your own ExecutorService (SwingWorker implements Runnable), you should then be able to use a PropertyChangeListener to monitor the state of the SwingWorker and when it's DONE, you can retrieve the value from it's get method without blocking