posdef
posdef

Reputation: 6532

SwingWorker hangs after completion: Is it bad practise to start and run a thread pool from a SwingWorker?

Assume an application that reads a datafile, gets some input (via GUI) from the user, gets additional data from a large database and carries out a lengthy analysis.

As advised, I removed the time-consuming parts (db-management and the actual analysis) into SwingWorker instances. Now the first part that carries out the DB management works fine (see code below):

dialog.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosed(WindowEvent e) {
            final File dbFile = dialog.getFile();

            frame.activateDisGlass("Loading DB, please wait...");
            SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    try {
                        dbman = new DbManager(dbFile);
                    } catch (SQLException e1) {
                        System.err.println("Unable to load the chosen database, see details below:");
                        e1.printStackTrace();
                    }
                    return null;
                }

                @Override
                protected void done(){
                    frame.deactivateDisGlass();
                }
            };              
            worker.execute();           
        }
    });

The same strategy does not work properly in the analysis case however. As the analysis piece is pretty lengthy and involves a lot of custom classes, I'll summarise the situation. The following piece of code lies in a custom class that interacts with the GUI to get user input, the calculations are triggered with the analyzeButton, which initiates creation of several objects one of which contains a thread-pool to carry out the independent analyses in parallel:

// INITIATE ANALYSIS
    analyzeButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {

            for (int i = 0; i < annots.length; i++) {
                annots[i] = (ANNOT_TYPE) combos[i].getSelectedItem();
            }

            // Validate the column annotations
            List<ANNOT_TYPE> annotlist = Arrays.asList(annots);
            if (!annotlist.contains(ANNOT_TYPE.R) &&
                !(annotlist.contains(ANNOT_TYPE.OS1) && 
                        annotlist.contains(ANNOT_TYPE.OS2))) {
                JOptionPane
                        .showMessageDialog(
                                null,
                                "<html>Missing annotation!<p>"
                                        + "<p> Please make sure you have annotated the datafile correctly...",
                                "Missing annotation!",
                                JOptionPane.ERROR_MESSAGE);
                return;
            }

            // Validate DB selection
            if (descPanel.getDBManager() == null) {
                JOptionPane
                        .showMessageDialog(
                                null,
                                "<html>No database not selected for analysis! <p>"
                                        + "<p> Please select and load a database..",
                                "Database error", JOptionPane.ERROR_MESSAGE);
                return;
            }

            // Activate progress indicator
            frame.getMainFrame().activateInfiGlass();

            SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() {
                    try {
                        // register parameters
                        param.addParam(AnalysisParams.value_key,descPanel.getValueTypeComboIndex());
                        param.addParam(AnalysisParams.sepchar_key,descPanel.getSepCharComboIndex());
                        paramPanel.registerParams();

                        StringBuilder sb = new StringBuilder("Data preview completed, initiating analysis...");
                        sb.append(System.lineSeparator())
                            .append("... column annotations: ")
                            .append(Arrays.toString(annots));
                        logger.info(sb.toString() + System.lineSeparator());

                        // Create dataset; to be passed on to SwingWorker which will
                        // execute the analysis
                        ds = new Dataset();

                        for (String[] line : data)
                            ds.addRow(line, annots);

                        System.out.println("Dataset parsed...");
                        logger.info("Dataset parsing complete "
                                + System.lineSeparator() 
                                + ds.toString()
                                + System.lineSeparator());

                        conserv = new ConcurrencyService(ds, descPanel.getDBManager());
                        conserv.serve();
                        DebugToolbox.dumpScores();                          
                        }
                    } catch (InterruptedException e) {
                        logger.severe("Concurrency service interrupted"
                                + System.lineSeparator()
                                + e.getStackTrace()
                                + System.lineSeparator());
                        System.err.println("Interrupt exception!!");
                    }
                    return null;
                }

                @Override
                protected void done() {
                    if(!conserv.isDone())
                        logger.warning("Concurrency Service is not done!" + 
                                System.lineSeparator());

                    logger.info("#DEBUG: Conserv should have been terminated by now..." + System.lineSeparator());
                    frame.getMainFrame().deactivateInfiGlass();
                }
            };

            worker.execute();

        }
    });

So what's the problem, you might ask... The problem is that done() is never called on the SwingWorker and the application practically just hangs, waiting for something. I can see that doInBackground() has completed since the scores are dumped into the logfile (which is the last piece of action in that method, but I don't see the expected output from done()

I got a thread dump from jvisualvm, and here's the relevant bit:

"SwingWorker-pool-1-thread-2" daemon prio=5 tid=0x00007f8ce5819000 nid=0x860b waiting on condition [0x0000000129593000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x0000000705a2c978> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

   Locked ownable synchronizers:
    - None

The previous SwingWorker (the one that loaded the DB) has the exact same state and stack trace, which makes me think that the SwingWorker has actually finished it's work, but the rest of the application is somehow not notified about it.

Similar questions: I have noticed two related questions with essential differences

  1. JDK-7 SwingWorker deadlocks? - my SwingWorker s are (or should be) independent of one another.

  2. SwingWorker hangs at Unsafe.park() - my SwingWorker does not interact with the GUI or return anything (other than null) so it should not have anything to do with get(), right?

Ideas?

Upvotes: 1

Views: 709

Answers (1)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

You state:

so it should not have anything to do with get(), right?

Not so. It is key to call get() otherwise you will not be able to catch and handle any exceptions thrown from within the SwingWorker's doInBackground method. So, I recommend that you don't forget to call get() on the SwingWorker in the done() method, perhaps call it first thing in the method, and don't forget to catch and analyze any and all exceptions thrown by this method.

Upvotes: 2

Related Questions