JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestBug1225EventQueueInterruptedAWT.java
Go to the documentation of this file.
1/**
2 * Copyright 2015 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 java.awt.BorderLayout;
32import java.awt.Color;
33import java.awt.Component;
34import java.awt.Dimension;
35import java.awt.Graphics;
36import java.awt.GridLayout;
37import java.awt.Label;
38import java.lang.Thread.UncaughtExceptionHandler;
39import java.lang.reflect.InvocationTargetException;
40
41import javax.swing.JFrame;
42import javax.swing.JPanel;
43import javax.swing.SwingUtilities;
44
45import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
46import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
47import com.jogamp.opengl.test.junit.util.MiscUtils;
48import com.jogamp.opengl.test.junit.util.UITestCase;
49
50import org.junit.Assert;
51
52import com.jogamp.common.ExceptionUtils;
53import com.jogamp.common.util.InterruptedRuntimeException;
54import com.jogamp.common.util.SourcedInterruptedException;
55import com.jogamp.nativewindow.NativeWindowFactory;
56import com.jogamp.opengl.GLAutoDrawable;
57import com.jogamp.opengl.GLEventListener;
58import com.jogamp.opengl.awt.GLCanvas;
59
60import org.junit.Test;
61import org.junit.FixMethodOrder;
62import org.junit.runners.MethodSorters;
63
64/**
65 * Test to check if interrupt on AWT-EventQueue causes a malfunction in JOGL.
66 * <p>
67 * After tests are displaying an ever color rotating rectangle in an AWT component alone
68 * and with an additional GearsES2 within a GLCanvas.
69 * </p>
70 * <p>
71 * The AWT component is issuing an interrupt during paint on the AWT-EDT.
72 * </p>
73 * <p>
74 * The reporter claims that an interrupt on the AWT-EDT shall not disturb neither AWT nor JOGL's GLCanvas
75 * and rendering shall continue.
76 * <ul>
77 * <li>This seems to be true for JRE 1.8.0_60</li>
78 * <li>This seems to be false for JRE 1.7.0_45. This JRE's AWT-EDT even dies occasionally when interrupted.</li>
79 * </ul>
80 * </p>
81 * <p>
82 * The test passes on GNU/Linux and Windows using JRE 1.8.0_60.
83 * </p>
84 */
85@FixMethodOrder(MethodSorters.NAME_ASCENDING)
87 static long durationPerTest = 1000; // ms
88
89 private void setVisible(final JFrame frame, final boolean v) throws InterruptedException, InvocationTargetException {
90 javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
91 public void run() {
92 frame.pack();
93 // frame.setSize(new Dimension(800, 600));
94 frame.setVisible(v);
95 }});
96 }
97 private void dispose(final JFrame jFrame) throws InterruptedException, InvocationTargetException {
98 SwingUtilities.invokeAndWait(new Runnable() {
99 public void run() {
100 jFrame.dispose();
101 } } ) ;
102 }
103
104
105 @Test(timeout=180000) // TO 3 min
106 public void test01_NoGL() throws InterruptedException, InvocationTargetException {
107 testImpl(false);
108 }
109
110 @Test(timeout=180000) // TO 3 min
111 public void test02_WithGL() throws InterruptedException, InvocationTargetException {
112 testImpl(true);
113 }
114
115 class OurUncaughtExceptionHandler implements UncaughtExceptionHandler {
116 public volatile Thread thread = null;
117 public volatile Throwable exception = null;
118
119 @Override
120 public void uncaughtException(final Thread t, final Throwable e) {
121 thread = t;
122 exception = e;
123 System.err.println("*** UncaughtException (this Thread "+Thread.currentThread().getName()+") : Thread <"+t.getName()+">, "+e.getClass().getName()+": "+e.getMessage());
124 ExceptionUtils.dumpThrowable("", e);
125 }
126 }
127 void testImpl(final boolean useGL) throws InterruptedException, InvocationTargetException {
128 if( !AWTRobotUtil.isAWTEDTAlive() ) {
129 System.err.println("Test aborted: AWT not alive");
130 return;
131 }
132 // Assume.assumeTrue("AWT not alive", AWTRobotUtil.isAWTEDTAlive());
133 // Assert.assertTrue("AWT not alive", AWTRobotUtil.isAWTEDTAlive());
134 final OurUncaughtExceptionHandler uncaughtHandler = new OurUncaughtExceptionHandler();
135 Thread.setDefaultUncaughtExceptionHandler( uncaughtHandler );
136
137 final Dimension csize = new Dimension(800, 400);
138 final JPanel panel = new JPanel(new GridLayout(2, 1));
139 final GLCanvas glc;
140 final InterruptableGLEL iglel;
141 if( useGL ) {
142 glc = new GLCanvas();
143 {
144 final GearsES2 gears = new GearsES2();
145 gears.setVerbose(false);
146 glc.addGLEventListener(gears);
147 }
148 iglel = new InterruptableGLEL();
149 glc.addGLEventListener(iglel);
150 glc.setSize(csize);
151 glc.setPreferredSize(csize);
152 panel.add(glc);
153 } else {
154 NativeWindowFactory.initSingleton();
155 glc = null;
156 iglel = null;
157 final Label l = new Label("No GL Object");
158 l.setSize(csize);
159 l.setPreferredSize(csize);
160 panel.add(l);
161 }
162 final InterruptingComponent icomp = new InterruptingComponent();
163 panel.add(icomp);
164 icomp.setSize(csize);
165 icomp.setPreferredSize(csize);
166
167 final JFrame frame = new JFrame();
168 frame.setResizable(true);
169 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
170
171 frame.getContentPane().add(panel, BorderLayout.CENTER);
172 setVisible(frame, true);
173 if( useGL ) {
174 Assert.assertTrue(AWTRobotUtil.waitForRealized(glc, true, null));
175 }
176 Assert.assertTrue(AWTRobotUtil.waitForRealized(icomp, true, null));
177
178 final InterruptableLoop loop = new InterruptableLoop(icomp, glc);
179 final Thread thread = new Thread(loop);
180
181 synchronized(loop) {
182 thread.start();
183 try {
184 loop.notifyAll(); // wake-up startup-block
185 while( !loop.isRunning && !loop.shallStop ) {
186 loop.wait(); // wait until started
187 }
188 loop.ack = true;
189 loop.notifyAll(); // wake-up startup-block
190 } catch (final InterruptedException e) {
191 Assert.assertNull("while starting loop", new InterruptedRuntimeException(e));
192 }
193 }
194
195 for(int i=0; thread.isAlive() && null == loop.exception && null == uncaughtHandler.exception && i<100; i++) {
196 icomp.interruptAWTEventQueue();
197 Thread.sleep(durationPerTest/100);
198 }
199
200 loop.shallStop = true;
201 synchronized(loop) {
202 try {
203 loop.notifyAll(); // wake-up pause-block (opt)
204 while( loop.isRunning ) {
205 loop.wait(); // wait until stopped
206 }
207 } catch (final InterruptedException e) {
208 Assert.assertNull("while stopping loop", new InterruptedRuntimeException(e));
209 }
210 }
211
212 //
213 // Notifications only!
214 //
215 // Note:
216 // On JRE 1.8.0_60: Interrupt is cleared on AWT-EDT
217 // On JRE 1.7.0_45: Interrupt is *NOT* cleared on AWT-EDT
218 //
219 if( null != iglel && null != iglel.exception ) {
220 ExceptionUtils.dumpThrowable("GLEventListener", iglel.exception);
221 }
222 if( null != icomp.exception ) {
223 ExceptionUtils.dumpThrowable("InterruptingComponent", icomp.exception);
224 }
225 if( null != loop.exception ) {
226 ExceptionUtils.dumpThrowable("loop", loop.exception);
227 }
228 if( null != uncaughtHandler.exception ) {
229 ExceptionUtils.dumpThrowable("uncaughtHandler", uncaughtHandler.exception);
230 }
231 if( !AWTRobotUtil.isAWTEDTAlive() ) {
232 System.err.println("AWT is not alive anymore!!! Ooops");
233 // cannot do anything anymore on AWT-EDT .. frame.dispose();
234 } else {
235 dispose(frame);
236 }
237
238 //
239 // Fail if interrupt was propagated to loop or uncaught handler
240 //
241 Assert.assertNull("Caught Exception in loop", loop.exception);
242 Assert.assertNull("Caught Exception via uncaughtHandler", uncaughtHandler.exception);
243 }
244
245 static class InterruptableLoop implements Runnable {
246 public volatile Exception exception = null;
247 public volatile boolean shallStop = false;
248 public volatile boolean isRunning = false;
249 public volatile boolean ack = false;
250 final InterruptingComponent icomp;
251 final GLCanvas glc;
252 boolean alt = false;;
253
254 InterruptableLoop(final InterruptingComponent icomp, final GLCanvas glc) {
255 this.icomp = icomp;
256 this.glc = glc;
257 }
258
259 public void stop() {
260 shallStop = true;
261 }
262
263 @Override
264 public void run()
265 {
266 synchronized ( this ) {
267 isRunning = true;
268 this.notifyAll();
269 try {
270 while( !ack ) {
271 this.wait(); // wait until ack
272 }
273 this.notifyAll();
274 } catch (final InterruptedException e) {
275 throw new InterruptedRuntimeException(e);
276 }
277 ack = false;
278 }
279 synchronized ( this ) {
280 try {
281 while( !shallStop ) {
282 if( alt ) {
283 icomp.repaint(); // issues paint of GLCanvas on AWT-EDT
284 } else if( null != glc ) {
285 // Avoid invokeAndWait(..) in GLCanvas.display() if AWT-EDT dies!
286 glc.repaint(); // issues paint of GLCanvas on AWT-EDT, which then issues display()!
287 }
288 alt = !alt;
289 Thread.sleep(16);
290 if( Thread.interrupted() ) {
291 final InterruptedRuntimeException e = new InterruptedRuntimeException(new InterruptedException("Interrupt detected in loop, thread: "+Thread.currentThread().getName()));
292 throw e;
293 }
294 }
295 } catch (final InterruptedException e) {
296 exception = SourcedInterruptedException.wrap(e);
297 ExceptionUtils.dumpThrowable("", exception);
298 } catch (final Exception e) {
299 exception = e;
300 ExceptionUtils.dumpThrowable("", exception);
301 } finally {
302 isRunning = false;
303 this.notifyAll();
304 }
305 }
306 }
307 }
308
309 static class InterruptableGLEL implements GLEventListener {
310 public volatile InterruptedException exception = null;
311 @Override
312 public void init(final GLAutoDrawable drawable) {
313 }
314 @Override
315 public void dispose(final GLAutoDrawable drawable) {
316 }
317 @Override
318 public void display(final GLAutoDrawable drawable) {
319 final Thread c = Thread.currentThread();
320 if( c.isInterrupted() && null == exception ) {
321 exception = new InterruptedException("Interrupt detected in GLEventListener, thread: "+c.getName());
322 drawable.removeGLEventListener(this);
323 }
324 }
325 @Override
326 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
327 }
328 }
329
330 static class InterruptingComponent extends Component {
331 private static final long serialVersionUID = 1L;
332 public volatile InterruptedException exception = null;
333
334 private volatile boolean doInterrupt = false;
335
336 private final Color[] colors =
337 new Color[] { Color.BLACK, Color.BLUE, Color.DARK_GRAY, Color.GRAY, Color.LIGHT_GRAY };
338 private int colorIdx = 0;
339
340 public InterruptingComponent() {
341 }
342
343 public void interruptAWTEventQueue() {
344 doInterrupt = true;
345 }
346
347 @Override
348 public void paint(final Graphics g)
349 {
350 final Thread c = Thread.currentThread();
351 if( c.isInterrupted() && null == exception ) {
352 exception = new InterruptedException("Interrupt detected in AWT Component, thread: "+c.getName());
353 }
354
355 g.setColor(colors[colorIdx++]);
356 if( colorIdx >= colors.length ) {
357 colorIdx = 0;
358 }
359 g.fillRect(0, 0, getWidth(), getHeight());
360
361 if(doInterrupt) {
362 System.err.println("Thread "+c.getName()+": *Interrupting*");
363 doInterrupt = false;
364 c.interrupt();
365 }
366 }
367 }
368
369 public static void main(final String[] args) {
370 for(int i=0; i<args.length; i++) {
371 if(args[i].equals("-time")) {
372 durationPerTest = MiscUtils.atol(args[++i], durationPerTest);
373 }
374 }
375 org.junit.runner.JUnitCore.main(TestBug1225EventQueueInterruptedAWT.class.getName());
376 }
377}
378
Test to check if interrupt on AWT-EventQueue causes a malfunction in JOGL.
static long atol(final String str, final long def)
Definition: MiscUtils.java:66