JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestGLCanvasAWTActionDeadlock01AWT.java
Go to the documentation of this file.
1/**
2 * Copyright 2012 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.opengl.test.junit.jogl.awt;
30
31import com.jogamp.opengl.GLAutoDrawable;
32import com.jogamp.opengl.GLCapabilities;
33import com.jogamp.opengl.GLEventListener;
34import com.jogamp.opengl.GLProfile;
35import com.jogamp.opengl.awt.GLCanvas;
36
37import com.jogamp.common.os.Platform;
38import com.jogamp.common.util.VersionNumber;
39import com.jogamp.common.util.awt.AWTEDTExecutor;
40import com.jogamp.opengl.util.Animator;
41import com.jogamp.opengl.util.AnimatorBase;
42import com.jogamp.opengl.util.FPSAnimator;
43
44import com.jogamp.opengl.test.junit.util.UITestCase;
45import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
46
47import com.jogamp.opengl.test.junit.util.MiscUtils;
48
49import java.applet.Applet;
50import java.awt.BorderLayout;
51import java.awt.Frame;
52import java.awt.Insets;
53import java.awt.event.WindowAdapter;
54import java.awt.event.WindowEvent;
55import java.lang.reflect.InvocationTargetException;
56
57import org.junit.Assert;
58import org.junit.Assume;
59import org.junit.Test;
60import org.junit.FixMethodOrder;
61import org.junit.runners.MethodSorters;
62
63/**
64 * BUG on OSX/CALayer w/ Java6:
65 * If frame.setTitle() is issued right after initialization the call hangs in
66 * <pre>
67 * at apple.awt.CWindow._setTitle(Native Method)
68 * at apple.awt.CWindow.setTitle(CWindow.java:765) [1.6.0_37, build 1.6.0_37-b06-434-11M3909]
69 * </pre>
70 * <p>
71 * OSX/CALayer is forced by using an Applet component in this unit test.
72 * </p>
73 * <p>
74 * Similar deadlock has been experienced w/ other mutable operation on an AWT Container owning a GLCanvas child,
75 * e.g. setResizable*().
76 * </p>
77 * <p>
78 * Users shall make sure all mutable AWT calls are performed on the EDT, even before 1st setVisible(true) !
79 * </p>
80 */
81@FixMethodOrder(MethodSorters.NAME_ASCENDING)
83 static long durationPerTest = 1000; // ms
84 static final int width = 512;
85 static final int height = 512;
86
87 GLEventListener gle1 = null;
88 GLEventListener gle2 = null;
89
90 @Test
91 public void test00NoAnimator() throws InterruptedException, InvocationTargetException {
92 testImpl(null, 0, false);
93 }
94
95 @Test
96 public void test01Animator() throws InterruptedException, InvocationTargetException {
97 testImpl(new Animator(), 0, false);
98 }
99
100 @Test
101 public void test02FPSAnimator() throws InterruptedException, InvocationTargetException {
102 testImpl(new FPSAnimator(30), 0, false);
103 }
104
105 @Test
106 public void test02FPSAnimator_RestartOnAWTEDT() throws InterruptedException, InvocationTargetException {
107 testImpl(new FPSAnimator(30), 200, false);
108 }
109
110 /** May crash due to invalid thread usage, i.e. non AWT-EDT
111 * @throws InvocationTargetException
112 * @throws InterruptedException
113 @Test
114 public void test02FPSAnimator_RestartOnCurrentThread() throws InterruptedException {
115 testImpl(new FPSAnimator(30), 200, true);
116 } */
117
118 private static void setFrameTitle(final Frame frame, final String msg) {
119 System.err.println("About to setTitle: <"+msg+"> CT "+Thread.currentThread().getName()+", "+
120 frame+", displayable "+frame.isDisplayable()+
121 ", valid "+frame.isValid()+", visible "+frame.isVisible());
122 // Thread.dumpStack();
123 AWTEDTExecutor.singleton.invoke(true, new Runnable() {
124 public void run() {
125 frame.setTitle(msg);
126 } } );
127 }
128
129 void testImpl(final AnimatorBase animator, final int restartPeriod, final boolean restartOnCurrentThread) throws InterruptedException, InvocationTargetException {
130 final Frame frame1 = new Frame("Frame 1");
131 final Applet applet1 = new Applet() {
132 private static final long serialVersionUID = 1L;
133 };
134
135 final VersionNumber version170 = new VersionNumber(1, 7, 0);
136 final boolean osxCALayerAWTModBug = Platform.OSType.MACOS == Platform.getOSType() &&
137 0 > Platform.getJavaVersionNumber().compareTo(version170);
138 System.err.println("OSX CALayer AWT-Mod Bug "+osxCALayerAWTModBug);
139 System.err.println("OSType "+Platform.getOSType());
140 System.err.println("Java Version "+Platform.getJavaVersionNumber());
141
142 Assert.assertNotNull(frame1);
143 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
144 public void run() {
145 frame1.setLayout(null);
146 frame1.pack();
147 {
148 final Insets insets = frame1.getInsets();
149 final int w = width + insets.left + insets.right;
150 final int h = height + insets.top + insets.bottom;
151 frame1.setSize(w, h);
152
153 final int usableH = h - insets.top - insets.bottom;
154 applet1.setBounds((w - width)/2, insets.top + (usableH - height)/2, width, height);
155 }
156 frame1.setLocation(0, 0);
157 frame1.setTitle("Generic Title");
158 frame1.add(applet1);
159 }});
160
161 frame1.addWindowListener(new WindowAdapter() {
162 public void windowClosing(final WindowEvent e) {
163 dispose(frame1, applet1);
164 }
165 });
166
167 gle1 = new GLEventListener() {
168 boolean justInitialized = true;
169
170 @Override
171 public void init(final GLAutoDrawable drawable) {
172 justInitialized = true;
173 if( !osxCALayerAWTModBug ) {
174 System.err.println("*Init*: CT "+Thread.currentThread().getName());
175 setFrameTitle(frame1, "INIT");
176 frame1.setResizable(false);
177 }
178 }
179
180 @Override
181 public void dispose(final GLAutoDrawable drawable) {
182 System.err.println("*Dispose*: CT "+Thread.currentThread().getName());
183 setFrameTitle(frame1, "DISPOSE");
184 }
185
186 @Override
187 public void display(final GLAutoDrawable drawable) {
188 if( !osxCALayerAWTModBug || !justInitialized ) {
189 System.err.println("*Display*: CT "+Thread.currentThread().getName());
190 setFrameTitle(frame1, "f "+frameCount+", fps "+( null != animator ? animator.getLastFPS() : 0));
191 frame1.setResizable(false);
192 }
193 frameCount++;
194 justInitialized = false;
195 }
196
197 @Override
198 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
199 if( !osxCALayerAWTModBug || !justInitialized ) {
200 System.err.println("*Reshape*: CT "+Thread.currentThread().getName());
201 setFrameTitle(frame1, "RESHAPE");
202 }
203 }
204 };
205 gle2 = new GearsES2();
206
207 GLCanvas glCanvas = createGLCanvas();
208 glCanvas.addGLEventListener(gle1);
209 glCanvas.addGLEventListener(gle2);
210
211 if(null != animator) {
212 System.err.println("About to start Animator: CT "+Thread.currentThread().getName());
213 animator.setUpdateFPSFrames(60, System.err);
214 animator.add(glCanvas);
215 animator.start();
216 }
217
218 attachGLCanvas(applet1, glCanvas, false);
219
220 System.err.println("About to setVisible.0 CT "+Thread.currentThread().getName());
221 try {
222 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
223 public void run() {
224 System.err.println("About to setVisible.1.0 CT "+Thread.currentThread().getName());
225 frame1.setVisible(true);
226 System.err.println("About to setVisible.1.X CT "+Thread.currentThread().getName());
227 }});
228 } catch (final Throwable t) {
229 t.printStackTrace();
230 Assume.assumeNoException(t);
231 }
232 System.err.println("About to setVisible.X CT "+Thread.currentThread().getName());
233
234 final long sleep = 0 < restartPeriod ? restartPeriod : 100;
235 long togo = durationPerTest;
236 while( 0 < togo ) {
237 if(null == animator) {
238 glCanvas.display();
239 }
240 if(0 < restartPeriod) {
241 glCanvas = restart(applet1, glCanvas, restartOnCurrentThread);
242 }
243
244 Thread.sleep(sleep);
245
246 togo -= sleep;
247 }
248
249 dispose(frame1, applet1);
250 if(null != animator) {
251 animator.stop();
252 }
253
254 gle1 = null;
255 gle2 = null;
256 }
257
258 int frameCount = 0;
259
260 GLCanvas createGLCanvas() {
261 System.err.println("*** createGLCanvas.0");
262 final GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
263 // Iff using offscreen layer, use pbuffer, hence restore onscreen:=true.
264 // caps.setPBuffer(true);
265 // caps.setOnscreen(true);
266 final GLCanvas glCanvas = new GLCanvas(caps);
267 glCanvas.setBounds(0, 0, width, height);
268 Assert.assertNotNull(glCanvas);
269 System.err.println("*** createGLCanvas.X");
270 frameCount = 0;
271 return glCanvas;
272 }
273
274 void dispose(final Frame frame, final Applet applet) {
275 try {
276 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
277 public void run() {
278 frame.remove(applet);
279 frame.dispose();
280 }});
281 } catch (final Throwable t) {
282 t.printStackTrace();
283 Assume.assumeNoException(t);
284 }
285 }
286
287 GLCanvas restart(final Applet applet, GLCanvas glCanvas, final boolean restartOnCurrentThread) throws InterruptedException {
288 glCanvas.disposeGLEventListener(gle1, true);
289 glCanvas.disposeGLEventListener(gle2, true);
290 detachGLCanvas(applet, glCanvas, restartOnCurrentThread);
291
292 glCanvas = createGLCanvas();
293
294 attachGLCanvas(applet, glCanvas, restartOnCurrentThread);
295 glCanvas.addGLEventListener(gle1);
296 glCanvas.addGLEventListener(gle2);
297
298 return glCanvas;
299 }
300
301 void attachGLCanvas(final Applet applet, final GLCanvas glCanvas, final boolean restartOnCurrentThread) {
302 System.err.println("*** attachGLCanvas.0 on-current-thread "+restartOnCurrentThread+", currentThread "+Thread.currentThread().getName());
303 if( restartOnCurrentThread ) {
304 applet.setLayout(new BorderLayout());
305 applet.add(glCanvas, BorderLayout.CENTER);
306 applet.validate();
307 } else {
308 try {
309 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
310 public void run() {
311 applet.setLayout(new BorderLayout());
312 applet.add(glCanvas, BorderLayout.CENTER);
313 applet.validate();
314 }});
315 } catch (final Throwable t) {
316 t.printStackTrace();
317 Assume.assumeNoException(t);
318 }
319 }
320 System.err.println("*** attachGLCanvas.X");
321 }
322
323 void detachGLCanvas(final Applet applet, final GLCanvas glCanvas, final boolean restartOnCurrentThread) {
324 System.err.println("*** detachGLCanvas.0 on-current-thread "+restartOnCurrentThread+", currentThread "+Thread.currentThread().getName());
325 if( restartOnCurrentThread ) {
326 applet.remove(glCanvas);
327 applet.validate();
328 } else {
329 try {
330 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
331 public void run() {
332 applet.remove(glCanvas);
333 applet.validate();
334 }});
335 } catch (final Throwable t) {
336 t.printStackTrace();
337 Assume.assumeNoException(t);
338 }
339 }
340 System.err.println("*** detachGLCanvas.X");
341 }
342
343 public static void main(final String args[]) {
344 for(int i=0; i<args.length; i++) {
345 if(args[i].equals("-time")) {
346 durationPerTest = MiscUtils.atoi(args[++i], (int)durationPerTest);
347 }
348 }
349 org.junit.runner.JUnitCore.main(TestGLCanvasAWTActionDeadlock01AWT.class.getName());
350 }
351}
BUG on OSX/CALayer w/ Java6: If frame.setTitle() is issued right after initialization the call hangs ...
static int atoi(final String str, final int def)
Definition: MiscUtils.java:57
An Animator subclass which attempts to achieve a target frames-per-second rate to avoid using all CPU...
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.