-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8339561: The test/jdk/java/awt/Paint/ListRepaint.java may fail after JDK-8327401 #20861
base: master
Are you sure you want to change the base?
Conversation
👋 Welcome back serb! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
ActiveAWTWindowTest test = new ActiveAWTWindowTest(); | ||
try { | ||
test.doTest(); | ||
} finally { | ||
EventQueue.invokeLater(() -> { | ||
EventQueue.invokeAndWait(() -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test should wait until the frame will be disposed.
"invokeLater" was added here,
private TextField textField, textField2; | ||
private volatile Frame frame, frame2; | ||
private volatile Button button, button2; | ||
private volatile TextField textField, textField2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably unnecessary but some of these fields might be used on different threads, so just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely, it's unnecessary. I don't mind though.
I'd rather refactor the test to simplify it. Using CountDownLatch
or other synchronisation primitives instead of wait/notify. I submitted JDK-8339791. Feel free to re-assign if you like.
@@ -66,16 +65,14 @@ public static void main(final String[] args) throws Exception { | |||
r = new Robot(); | |||
KeyEventsTest app = new KeyEventsTest(); | |||
try { | |||
EventQueue.invokeAndWait(app::initAndShowGui); | |||
app.initAndShowGui(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test was changed here, the root cause of the failure discussed in https://github.com/openjdk/jdk/pull/19339/files#r1609189736 is a product bug: https://bugs.openjdk.org/browse/JDK-8201307.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it's a product bug…
AWT components do not explicitly state whether they're thread-safe or not. It's more or less assumed they should be. At the same time, I don't see code which ensures all the cases are thread-safe.
As far as I know, clientlibs group has been treating AWT components just like Swing ones: for safety create, access and destroy AWT components on EDT only.
I haven't looked at the test code thoroughly. It looks it still needs more investigation.
At the same time, I find it weird that repaint
paints directly in its code and then posts paint event.
jdk/src/java.desktop/unix/classes/sun/awt/X11/XListPeer.java
Lines 389 to 390 in 77468c2
painter.paint(g, firstItem, lastItem, options, source, distance); | |
postPaintEvent(target, 0, 0, getWidth(), getHeight()); |
Overall, it needs more investigation. It could be that the fix for JDK-6471693 needs revising.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the same time, I find it weird that repaint paints directly in its code and then posts paint event.
This is one of the implementation details of the AWT, the "native" component should be painted before the paint event will proceed on EDT, and even if EDT is blocked the "native" part should be painted. Since the XAWT is implemented via Swing(or some custom "java peers") which are not thread-safe we should use some additional synchronization. On macOS we have a special lock for that
synchronized (getDelegateLock()) { |
final Object getDelegateLock() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the NPE is triggered for List.select only because it is one method in the test that uses peer w/o synchronization.
// Bug #4059614: select can't be synchronized while calling the peer, |
try { | ||
EventQueue.invokeLater(ListRepaint::createAndShowGUI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of the test is to run some methods of the AWT component on the main thread and check if it will be refreshed on the EDT. It was changed here. It is a product bug: https://bugs.openjdk.org/browse/JDK-8201307.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, the previous update has changed the logic of the test so that it no longer reproduces the original problem for which it was written, namely JDK-7090424.
@@ -81,10 +82,11 @@ public static void main(String[] args) throws Exception { | |||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); | |||
robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); | |||
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); | |||
robot.delay(3000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test was discussed here. It seems that the purpose of the test is to check that no events will be generated while the left button is pressed, so we should check that "ActionListener" is not triggered and then release the mouse button.
Thoughts?
Webrevs
|
private TextField textField, textField2; | ||
private volatile Frame frame, frame2; | ||
private volatile Button button, button2; | ||
private volatile TextField textField, textField2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely, it's unnecessary. I don't mind though.
I'd rather refactor the test to simplify it. Using CountDownLatch
or other synchronisation primitives instead of wait/notify. I submitted JDK-8339791. Feel free to re-assign if you like.
@@ -51,7 +50,7 @@ | |||
import jdk.test.lib.Platform; | |||
|
|||
public class KeyEventsTest { | |||
TestState currentState; | |||
private volatile TestState currentState; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not declare it a volatile
. It could give a false impression of being thread-safe but it is not. The volatile
modifier has a meaning only when it's written and read subsequently. If the reference doesn't change, it has no effect on the visibility of the internal object state.
The value is assigned to currentState
while holding a lock LOCK
.
At the same time, currentState.setAction(true)
is called without any synchronisation and adding volatile
won't make the change of the state thread-safe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitted JDK-8340196: j.a/List/KeyEventsTest/KeyEventsTest: TestState currentState is not thread-safe.
@@ -66,16 +65,14 @@ public static void main(final String[] args) throws Exception { | |||
r = new Robot(); | |||
KeyEventsTest app = new KeyEventsTest(); | |||
try { | |||
EventQueue.invokeAndWait(app::initAndShowGui); | |||
app.initAndShowGui(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it's a product bug…
AWT components do not explicitly state whether they're thread-safe or not. It's more or less assumed they should be. At the same time, I don't see code which ensures all the cases are thread-safe.
As far as I know, clientlibs group has been treating AWT components just like Swing ones: for safety create, access and destroy AWT components on EDT only.
I haven't looked at the test code thoroughly. It looks it still needs more investigation.
At the same time, I find it weird that repaint
paints directly in its code and then posts paint event.
jdk/src/java.desktop/unix/classes/sun/awt/X11/XListPeer.java
Lines 389 to 390 in 77468c2
painter.paint(g, firstItem, lastItem, options, source, distance); | |
postPaintEvent(target, 0, 0, getWidth(), getHeight()); |
Overall, it needs more investigation. It could be that the fix for JDK-6471693 needs revising.
@@ -34,15 +34,21 @@ public final class ButtonRepaint extends Button { | |||
|
|||
public static void main(final String[] args) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could expand imports, remove @author
tag and the second *
from /**
?
@@ -33,15 +33,21 @@ public final class CheckboxRepaint extends Checkbox { | |||
|
|||
public static void main(final String[] args) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could expand imports, remove @author
tag and the second *
from /**
?
|
||
if (!passed) { | ||
if (!result) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, we should ensure that an ActionEvent
gets delivered at this point. Is it expected or not?
button.addActionListener(e -> { | ||
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) | ||
!= InputEvent.BUTTON1_MASK) { | ||
System.out.println("Status: Failed"); | ||
button.addActionListener(new ActionListener() { | ||
public void actionPerformed(ActionEvent e) { | ||
passed = false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The updated code here is correct; Alexander has restored the condition that was present in the original test which was closed-source and manual.
However, it may be not necessary. Instead, the test should fail immediately if ActionEvent
contains anything but BUTTON1_DOWN_MASK
. (Well, cleaning up robot state to release mouse buttons is a good thing to do.)
!= InputEvent.BUTTON1_MASK) { | ||
System.out.println("Status: Failed"); | ||
button.addActionListener(new ActionListener() { | ||
public void actionPerformed(ActionEvent e) { | ||
passed = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use two CountDownLatch
es:
if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
!= InputEvent.BUTTON1_MASK) {
wrongMouseButton.countDown();
} else {
mouseButton1.countDown();
}
Then in main:
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK);
// robot.delay(3000);
if (wrongMouseButton.await(2, TimeUnit.SECONDS)) {
// Restore robot state
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
throw new RuntimeException("ActionEvent delivered from mouse button 3");
}
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
if (mouseButton1.await(2, TimeUnit.SECONDS)) {
throw new RuntimeException("ActionEvent not delivered from mouse button 1");
}
@@ -53,10 +56,8 @@ public static void main(String[] args) throws Exception { | |||
frame = new JFrame("bug4490179"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of three volatile fields — pt
, buttonW
, buttonH
— there could be only one:
SwingUtilities.invokeAndWait(() -> {
Point loc = button.getLocationOnScreen();
Dimension size = button.getSize();
pt = new Point(loc.x + size.width / 2,
loc.y + size.height / 2);
});
I think this better conveys the idea, less variables / fields to track in your mind.
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
}); | ||
|
||
if (!bMenuSelected) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also expand imports and remove @author
tag?
robot.delay(3000); | ||
boolean result = passed; | ||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); | ||
if (anyButton.await(3, TimeUnit.SECONDS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should 2 seconds be enough?
robot.waitForIdle(); | ||
robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); | ||
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); | ||
|
||
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); | ||
robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); | ||
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); | ||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); | ||
robot.delay(500); | ||
if (anyButton.await(3, TimeUnit.SECONDS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await
is also throws the InterruptedException
, so theoretically we can leave the mouse button pressed in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
await
throws InterruptedException
when the thread is interrupted; the main thread is never interrupted in the test, therefore InterruptedException
is never thrown.
@mrserb This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Several tests modified by #19339 have been tweaked, see inline comments.
Notes:
@azvegint @aivanov-jdk please take a look.
Progress
Issue
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20861/head:pull/20861
$ git checkout pull/20861
Update a local copy of the PR:
$ git checkout pull/20861
$ git pull https://git.openjdk.org/jdk.git pull/20861/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 20861
View PR using the GUI difftool:
$ git pr show -t 20861
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20861.diff
Webrev
Link to Webrev Comment