Interference/timing(?) issue on macOS when creating and destroying GLCanvas

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Interference/timing(?) issue on macOS when creating and destroying GLCanvas

sailsman63
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?
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

gouessej
Administrator
Hello

Is it acceptable for you to use SwingUtilities.invokeLater() until we find a more reliable solution?
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

Martin
In reply to this post by sailsman63
Hi,

I was curious about this hang, so I tried reproducing on macOS Big Sur on Silicon (ARM).

I tried following JDK :
Azul JDK 17 for ARM
OpenJDK 17 (Rosetta might be involved to run on ARM)
OpenJDK 11 (Rosetta as well)

First program hangs for any JDK.
Second program never hangs for any JDK.  
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

gouessej
Administrator
Thank you so much for this finding!
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

sailsman63
In reply to this post by gouessej
It might be possible to patch around for the time being. I have in mind how to make the method where this happens a little more robust. I'll get back to you once we have a chance to test.

Since the question of exact platform came up, I asked our mac person, and the results we are seeing are on



macOS Big Sur 11.6.1
Mac mini
Processor: 2.6 GHz Dual-Core Intel Core i5
Graphics: Intel Iris 1536MB
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

sailsman63
Just a quick update:

We have been able to patch around this issue for the time being, mostly by having the dispose() method be called from a SwingWorker that sleeps for about a second before running.

Let me know if there's any other information that we can try to find for you.
Reply | Threaded
Open this post in threaded view
|

Re: Interference/timing(?) issue on macOS when creating and destroying GLCanvas

Martin
Thanks! That would be great to have the code of your workaround :)