Interference/timing(?) issue on macOS when creating and destroying GLCanvas
Posted by sailsman63 on Nov 27, 2021; 7:13am
URL: https://forum.jogamp.org/Interference-timing-issue-on-macOS-when-creating-and-destroying-GLCanvas-tp4041505.html
I'm one of the lead maintainers for ArtOfIllusion, a 3d modeling/rendering package. One of our mac users has recently been helping troubleshoot issues that we've had with our macOS package. They identified two situations where creating and/or destroying a GLJPanel in close proximity to other AWT/Swing-ish commands will cause the application to hang with, so far, no known error messages.
The test system in question is running Big Sur with OpenJDK-17.0.1
These are distilled-down versions. If the code looks odd, it's because we have clipped out all the application-specific context for why these operations are done in this order.
We speculate that there may be a timing component because wrapping some of the operations in nested SwingUtilities.invokeLater() will allow the application to proceed without hanging.
First Test:
import com.jogamp.opengl.awt.GLJPanel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MaximizeTest2 {
JFrame f;
GLJPanel canvas;
JPanel p;
public MaximizeTest2() {
newWindow();
}
public void newWindow() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
f = new JFrame("test frame maximized both ");
f.setExtendedState(JFrame.MAXIMIZED_BOTH); //This call is about the only thing that reliably
f.setVisible(true); //triggers this behavior, but it actually came
//up in the real application
p = new JPanel();
canvas = new GLJPanel();
//p.add(canvas); //Note: It does not seem to matter if canvas is added to the panel or not.
f.setContentPane(p);
f.validate();
System.out.println("yyyy");
}
});
}
public static void main(String[] args) {
// TODO code application logic here
System.out.println("xxxx");
new MaximizeTest2();
System.out.println("zzzz");
}
}
This behavior has improved between the rc-20200307 build and rc-20210111 - it used to be much easier to trigger. Possibly related to the fixes for Bug-1398?
Second Test:
(and this one is a more current concern - the other we can patch around. It is also a little longer, as so far it has only reproduced with ActionEvents involved in the mix)
import com.jogamp.opengl.awt.GLJPanel;
import java.awt.Frame;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
/**
* run and then select File->New
* disposes previous frame with a GLJPanel
* just after creating new frame with GLJPanel hangs
* openJDK 17.0.1 - macos Big Sur
* JOGL 2.4.0
*/
public class DisposeTest2 {
ArrayList<JFrame> frames = new ArrayList();
ArrayList<GLJPanel> gljpanels = new ArrayList();
boolean disposeFrame = false;
int frameID = 1;
DisposeTest2() {
NewWindowDisposeOldTest();
}
public void NewWindowDisposeOldTest() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// NOTE: if we move the destroy logic up here, before creating a new frame, the system will not hang
JFrame f1 = new JFrame("Frame " + (frameID++));
addMenu(f1);
GLJPanel canvas1 = new GLJPanel();
frames.add(f1);
gljpanels.add(canvas1);
JPanel p1 = new JPanel();
// without adding the GLJPanel the hang does not happen
p1.add(canvas1);
f1.setContentPane(p1);
f1.setBounds(0, 0, 300, 100);
// without this line the hang does not happen !!!
f1.setExtendedState(Frame.MAXIMIZED_BOTH);
f1.setVisible(true);
for (int i = 0; i < frames.size() - 1; i++) { //Ignores most recent frame - hacky, but short
if (disposeFrame) {
frames.get(i).dispose(); // hangs
/* All these alternate options also cause a hang
*frames.get(i).getContentPane().removeAll();
*frames.get(i).setContentPane(new JPanel()); //replacing the ContentPane instead of frame dispose also hangs
*gljpanels.get(i).destroy(); // destory the GLJPanel instead of frame dispose also hangs
*/
} else {
frames.get(i).validate();
frames.get(i).repaint();
}
frames.remove(i);
gljpanels.remove(i);
}
}
}
});
}
public void addMenu(JFrame frame) {
JMenuBar bar = new JMenuBar();
JMenu menu;
JMenuItem item;
menu = new JMenu("File");
menu.setMnemonic('f');
item = new JMenuItem("New");
item.setMnemonic('N');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
NewWindowDisposeOldTest();
}
});
menu.add(item);
item = new JMenuItem("Quit");
item.setMnemonic('Q');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.exit(0);
}
});
menu.add(item);
bar.add(menu);
frame.setJMenuBar(bar);
}
public static void main(String[] args) {
new DisposeTest2();
}
}
Thoughts on what might be going wrong here, or are we just running too many swing commands together?