Login  Register

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?