Hi Martin,

 

Excellent assessment of the problem and great explanation of it. Appreciate 
your efforts in debugging and proposing a solution to this problem.

 

I have couple questions:

1.       This problem (including the one described in JDK-8152974) is not 
reproducible at all in Mac. I would like to understand why this happens only on 
Windows/Linux because the proposed fix is in generic code.

2.       I’m not sure if I agree to your proposal of dispatching 
non-SequencedEvents, from the queue. The events arriving after a particular 
SequencedEvent could be dependent on this event – for example, the current 
SequencedEvent could be a focus change event, and the subsequent events could 
be Key events. So, as per your solution, if we dispatch them, there is a 
possibility that the intended component may not receive those events.

 

Thanks,
Krishna

 

From: Martin Balao <mba...@redhat.com> 
Sent: Tuesday, October 16, 2018 5:18 PM
To: Laurent Bourgès <bourges.laur...@gmail.com>
Cc: awt-dev@openjdk.java.net
Subject: Re: <AWT Dev> OpenJdk11-28-EA JDialog hanging

 

Hi Laurent,

 

Thanks for your test! Great job :-)

 

I applied some minor changes for integration:

 

 * Renamed to TestSeqEventsMultipleContexts

  * A bit longer but should describe what this is about

  * Placed in jdk/java/awt/event/SequencedEvent

 

 * Encapsulated the window in TestWindow class

 

 * Instead of waiting 1s for windows to show, loop until windows are shown -or 
the test hard stops-. When I ran in my environment, 1s was not enough. This 
should make the test more resilent. We still depend on a timer but that is 
inevitably.

 

 * Incremented the time we wait for the TGs to finish. 2s was not enough in my 
runs. However, we now do frequent checks to finish earlier in the success path.

 

 * Calculated expected value in initilization time

 

 * Changed a bit how the test finishes

  * System.exit(0) is a failure for jtreg

  * We need status -1 for jtreg to detect failures, and will do a hard stop 
when time expires

  * Stack traces for the main thread are not much relevant 

  * dispose window to exit gracefully on the success path

 

 * Removed unused code (button click action)

 

 * Added jtreg tags & copyright

 

Webrev.02 with test integrated:

 

 * http://cr.openjdk.java.net/~mbalao/webrevs/8204142/8204142.webrev.02

 * http://cr.openjdk.java.net/~mbalao/webrevs/8204142/8204142.webrev.02.zip

 

Kind regards,

Martin.-

 

On Mon, Oct 15, 2018 at 3:38 PM, Laurent Bourgès <HYPERLINK 
"mailto:bourges.laur...@gmail.com"bourges.laur...@gmail.com> wrote:

Hi Martin,

Thanks for your feedback and happy to hear that it worked!

 

In regards to pendingEvents sync, my understanding is that SequencedEvent 
objects will be posted to one EventQueue only and, thus, be dispatched by one 
EDT.

 

Agreed. 

 

 

I had a quick look at "Exception in thread "AWT-EventQueue-1" 
java.lang.IllegalArgumentException: null source" exception and could not find 
any connection to the code modified in the context of this bug. Apparently, 
"activeWindow" variable in "WindowEvent.WINDOW_LOST_FOCUS" case is null 
(DefaultKeyboardFocusManager.dispatchEvent function). I thought that this was 
caused by the fact that the test is injecting events that modify the focus on 
both windows at a very high rate and, by the time DefaultKeyboardFocusManager 
dispatches the event, it could be too late. Just an hypothesis.

 

I agree, it is certainly a side-effect: other issues may be triggered by such 
event intensive test.

 

Look forward to your re-written test.

 

Here it is:


import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.JButton;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

/*
 * Running this code causes the AWT Event Queues to be blocked on OpenJDK11
 * javac --add-exports java.desktop/sun.awt=ALL-UNNAMED TestWinEvent.java
 *
 * @author Laurent Bourges
 */
public final class TestWinEvent extends JFrame implements ActionListener {

    private static final long serialVersionUID = 1L;

    private static final int NUM_WINDOW = 2;
    private static final int INTERVAL = 50;
    private static final int MAX_TIME = 10000; // 10s
    private static final int MAX_COUNT = MAX_TIME / INTERVAL;
    private static final List<TestWinEvent> WINDOWS = new 
ArrayList<TestWinEvent>();

    private final JButton btn;
    private int counter = 0;
    private final Timer t;

    public static void main(String[] args) {
        try {
            for (int i = 0; i < NUM_WINDOW; i++) {
                createWin(i + 1);
            }

            // Wait MAX_TIME + 2s
            Thread.sleep(MAX_TIME + 2000);

            int total = 0;
            for (TestWinEvent window : WINDOWS) {
                total += window.getCounter();
            }

            // Failure if AWT hanging: assert
            final int expected = MAX_COUNT * NUM_WINDOW;
            if (total != expected) {
                throw new IllegalStateException("Total [" + total + "] != 
expected [" + expected + "] !");
            }
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } catch (IllegalStateException iae) {
            iae.printStackTrace();
        } finally {
            System.exit(0);
        }
    }

    private static void createWin(int tgNum) {
        new Thread(new ThreadGroup("TG " + tgNum),
                new Runnable() {
            @Override
            public void run() {
                sun.awt.SunToolkit.createNewAppContext();

                final AtomicReference<TestWinEvent> ref = new 
AtomicReference<TestWinEvent>();

                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        final TestWinEvent window = new TestWinEvent(tgNum);
                        window.setVisible(true);
                        ref.set(window);
                        WINDOWS.add(window);
                    }
                });

                try {
                    // Wait 1s to show window
                    Thread.sleep(1000);
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }

                final TestWinEvent window = ref.get();
                if (window != null) {
                    window.enableTimer(true);
                }
            }
        }).start();
    }

    TestWinEvent(final int num) {
        super("Test Window [" + num + "]");
        setMinimumSize(new Dimension(300, 200));
        setLocation(100 + 400 * (num - 1), 100);

        setLayout(new BorderLayout());
        JLabel textBlock = new JLabel("Lorem ipsum dolor sit amet...");
        add(textBlock);

        btn = new JButton("TEST");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button#" + num + " clicked: " + counter);
            }

        });
        add(btn, BorderLayout.SOUTH);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();

        t = new Timer(INTERVAL, this);
        t.setRepeats(false);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        this.toFront();
        btn.setText("TEST " + (++counter));
        this.toBack();
        if (counter < MAX_COUNT) {
            enableTimer(true);
        } else {
            setVisible(false);
        }
    }

    void enableTimer(boolean enable) {
        if (enable) {
            t.start();
        } else {
            t.stop();
        }
    }

    int getCounter() {
        return counter;
    }
}

 

I will attach it in the JBS bug.

 

Regards,

Laurent

 

Reply via email to