JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestNewtCanvasSWTBug628ResizeDeadlockAWT.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.swt;
30
31import java.awt.AWTException;
32import java.awt.Robot;
33import java.lang.reflect.InvocationTargetException;
34
35import org.eclipse.swt.SWT ;
36import org.eclipse.swt.layout.FillLayout ;
37import org.eclipse.swt.widgets.Composite ;
38import org.eclipse.swt.widgets.Display ;
39import org.eclipse.swt.widgets.Shell ;
40import org.junit.Assert;
41import org.junit.Assume;
42import org.junit.Test;
43import org.junit.FixMethodOrder;
44import org.junit.runners.MethodSorters;
45
51import com.jogamp.opengl.GLProfile;
52import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
53import com.jogamp.common.util.InterruptSource;
54import com.jogamp.common.util.InterruptedRuntimeException;
55import com.jogamp.nativewindow.swt.SWTAccessor;
56import com.jogamp.newt.NewtFactory;
57import com.jogamp.newt.event.KeyAdapter;
58import com.jogamp.newt.event.KeyEvent;
61import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
62import com.jogamp.opengl.test.junit.util.MiscUtils;
63import com.jogamp.opengl.test.junit.util.NewtTestUtil;
64import com.jogamp.opengl.test.junit.util.SWTTestUtil;
65import com.jogamp.opengl.test.junit.util.TestUtil;
66import com.jogamp.opengl.test.junit.util.UITestCase;
67
68////////////////////////////////////////////////////////////////////////////////
69
70
71@FixMethodOrder(MethodSorters.NAME_ASCENDING)
73
74 static int duration = 500;
75
76 static class BigFlashingX implements GLEventListener
77 {
78 float r = 0f, g = 0f, b = 0f;
79
80 @Override
81 public void init( final GLAutoDrawable drawable )
82 {
83 final GL2 gl = drawable.getGL().getGL2() ;
84
85 gl.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) ;
86
88 gl.glEnable( GL.GL_BLEND ) ;
90 }
91
92 @Override
93 public void reshape( final GLAutoDrawable drawable, final int x, final int y, final int width, final int height )
94 {
95 // System.err.println( ">>>>>>>> reshape " + x + ", " + y + ", " + width + ", " +height ) ;
96 final GL2 gl = drawable.getGL().getGL2() ;
97
98 gl.glViewport( 0, 0, width, height ) ;
99
101 gl.glLoadIdentity() ;
102 gl.glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 ) ;
103
105 gl.glLoadIdentity() ;
106 }
107
108 @Override
109 public void display( final GLAutoDrawable drawable )
110 {
111 // System.err.println( ">>>> display" ) ;
112 final GL2 gl = drawable.getGL().getGL2() ;
113
114 // Sven: I could have been seeing things, but it seemed that if this
115 // glClear is in here twice it seems aggravates the problem. Not
116 // sure why other than it just takes longer, but this is pretty
117 // fast operation.
120
121 gl.glColor4f( r, g, b, 1.0f ) ;
122
123 gl.glBegin( GL.GL_LINES ) ;
124 {
125 gl.glVertex2f( -1.0f, 1.0f ) ;
126 gl.glVertex2f( 1.0f, -1.0f ) ;
127
128 gl.glVertex2f( -1.0f, -1.0f ) ;
129 gl.glVertex2f( 1.0f, 1.0f ) ;
130 }
131 gl.glEnd() ;
132
133 if(r<1f) {
134 r+=0.1f;
135 } else if(g<1f) {
136 g+=0.1f;
137 } else if(b<1f) {
138 b+=0.1f;
139 } else {
140 r = 0f;
141 g = 0f;
142 b = 0f;
143 }
144 }
145
146 @Override
147 public void dispose( final GLAutoDrawable drawable )
148 {
149 }
150 }
151
152 ////////////////////////////////////////////////////////////////////////////////
153
154 static class ResizeThread extends InterruptSource.Thread {
155 volatile boolean shallStop = false;
156 private final Shell _shell ;
157 private int _n ;
158
159 public ResizeThread( final Shell shell )
160 {
161 _shell = shell ;
162 }
163
164 final Runnable resizeAction = new Runnable() {
165 @Override
166 public void run()
167 {
168 System.err.println("[R-i shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]");
169 if( shallStop || _shell.isDisposed() ) {
170 return;
171 }
172 try {
173 if( _n % 2 == 0 ) {
174 _shell.setSize( 200, 200 ) ;
175 } else {
176 _shell.setSize( 400, 450 ) ;
177 }
178 } catch (final Exception e0) {
179 e0.printStackTrace();
180 Assert.assertTrue("Deadlock @ setSize: "+e0, false);
181 }
182 ++_n ;
183 } };
184
185 @Override
186 public void run()
187 {
188 // The problem was originally observed by grabbing the lower right
189 // corner of the window and moving the mouse around rapidly e.g. in
190 // a circle. Eventually the UI will hang with something similar to
191 // the backtrace noted in the bug report.
192 //
193 // This loop simulates rapid resizing by the user by toggling
194 // the shell back-and-forth between two sizes.
195
196 System.err.println("[R-0 shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]");
197
198 final Display display = _shell.getDisplay();
199
200 while( !shallStop && !_shell.isDisposed() )
201 {
202 try
203 {
204 System.err.println("[R-n shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]");
205 display.asyncExec( resizeAction );
206 display.wake();
207
208 java.lang.Thread.sleep( 50L ) ;
209 } catch( final InterruptedException e ) {
210 throw new InterruptedRuntimeException(e);
211 }
212 }
213 System.err.println("*R-Exit* shallStop "+shallStop+", disposed "+_shell.isDisposed());
214 }
215 }
216
217 ////////////////////////////////////////////////////////////////////////////////
218
219 static class KeyfireThread extends InterruptSource.Thread
220 {
221 volatile boolean shallStop = false;
222 Display _display;
223 Robot _robot;
224 int _n = 0;
225
226 public KeyfireThread(final Robot robot, final Display display)
227 {
228 super();
229 _robot = robot;
230 _display = display;
231 }
232
233 @Override
234 public void run()
235 {
236 System.err.println("[K-0]");
237
238 while( !shallStop )
239 {
240 try {
241 System.err.println("[K-"+_n+"]");
243 AWTRobotUtil.newtKeyPress(_n, _robot, true, KeyEvent.VK_0, 10);
244 AWTRobotUtil.newtKeyPress(_n, _robot, false, KeyEvent.VK_0, 0);
245 java.lang.Thread.sleep( 40L ) ;
246 _n++;
247 if(!_display.isDisposed()) {
248 _display.wake();
249 }
250 } catch( final InterruptedException e ) {
251 break ;
252 }
253 }
254 System.err.println("*K-Exit*");
255 }
256 }
257
258 ////////////////////////////////////////////////////////////////////////////////
259
260 private volatile boolean shallStop = false;
261
262 static class SWT_DSC {
263 volatile Display display;
264 volatile Shell shell;
265 volatile Composite composite;
266 volatile com.jogamp.newt.Display swtNewtDisplay = null;
267
268 public void init() {
269 SWTAccessor.invokeOnOSTKThread(true, new Runnable() {
270 @Override
271 public void run() {
272 display = new Display();
273 Assert.assertNotNull( display );
274 }});
275
276 display.syncExec(new Runnable() {
277 @Override
278 public void run() {
279 shell = new Shell( display );
280 Assert.assertNotNull( shell );
281 shell.setLayout( new FillLayout() );
282 composite = new Composite( shell, SWT.NO_BACKGROUND );
283 composite.setLayout( new FillLayout() );
284 Assert.assertNotNull( composite );
285 }});
286 swtNewtDisplay = NewtFactory.createDisplay(null, false); // no-reuse
287 }
288
289 public void dispose() {
290 Assert.assertNotNull( display );
291 Assert.assertNotNull( shell );
292 Assert.assertNotNull( composite );
293 try {
294 display.syncExec(new Runnable() {
295 @Override
296 public void run() {
297 composite.dispose();
298 shell.dispose();
299 }});
300 SWTAccessor.invokeOnOSTKThread(true, new Runnable() {
301 @Override
302 public void run() {
303 display.dispose();
304 }});
305 }
306 catch( final Throwable throwable ) {
307 throwable.printStackTrace();
308 Assume.assumeNoException( throwable );
309 }
310 swtNewtDisplay = null;
311 display = null;
312 shell = null;
313 composite = null;
314 }
315 }
316
317 @Test
318 public void test() throws InterruptedException, AWTException, InvocationTargetException {
319 /**
320 * Use AWT _after_ SWT (4.26) or else .. (on GTK/X11, OpenJDK 17):
321 * (java:786260): GLib-GObject-WARNING **: 04:32:25.870: cannot register existing type 'GdkDisplayManager'
322 * (java:786260): GLib-CRITICAL **: 04:32:25.870: g_once_init_leave: assertion 'result != 0' failed
323 * (java:786260): GLib-GObject-CRITICAL **: 04:32:25.870: g_object_new_with_properties: assertion 'G_TYPE_IS_OBJECT (object_type)' failed
324 * SIGSEGV (0xb) at pc=0x00007faabf781b30, pid=786260, tid=786261
325 *
326 * JRE version: OpenJDK Runtime Environment (17.0.4+8) (build 17.0.4+8-Debian-1deb11u1)
327 * Java VM: OpenJDK 64-Bit Server VM (17.0.4+8-Debian-1deb11u1, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
328 * Problematic frame:
329 * C [libgdk-3.so.0+0x38b30] gdk_display_manager_get_default_display+0x0
330 *
331 * Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
332 * C [libgdk-3.so.0+0x38b30] gdk_display_manager_get_default_display+0x0
333 * j org.eclipse.swt.internal.gtk.OS.isX11()Z+6
334 * j org.eclipse.swt.internal.gtk.OS.<clinit>()V+1929
335 * v ~StubRoutines::call_stub
336 * V [libjvm.so+0x81b665]
337 * V [libjvm.so+0x7faa0c]
338 * V [libjvm.so+0x7fae23]
339 * V [libjvm.so+0x8eb27e]
340 * V [libjvm.so+0x8ee3c4] JVM_FindClassFromCaller+0x124
341 * C [libjava.so+0xd138] Java_java_lang_Class_forName0+0xc8
342 * j java.lang.Class.forName0(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;+0 java.base@17.0.4
343 * j java.lang.Class.forName(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;+43 java.base@17.0.4
344 * j com.jogamp.common.util.ReflectionUtil.getClassImpl(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;+209
345 * j com.jogamp.common.util.ReflectionUtil.getClass(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;+3
346 * j com.jogamp.nativewindow.swt.SWTAccessor.<clinit>()V+785
347 */
348 // final Robot robot = new Robot();
349
350 final SWT_DSC dsc = new SWT_DSC();
351 dsc.init();
352
353 final Robot robot = new Robot();
354
355 final GLWindow glWindow;
356 {
357 final GLProfile gl2Profile = GLProfile.get( GLProfile.GL2 ) ;
358 final GLCapabilities caps = new GLCapabilities( gl2Profile ) ;
359 final com.jogamp.newt.Screen screen = NewtFactory.createScreen(dsc.swtNewtDisplay, 0);
360 glWindow = GLWindow.create( screen, caps ) ;
361 glWindow.addGLEventListener( new BigFlashingX() ) ;
362 glWindow.addKeyListener(new KeyAdapter() {
363 @Override
364 public void keyReleased(final com.jogamp.newt.event.KeyEvent e) {
365 if( !e.isPrintableKey() || e.isAutoRepeat() ) {
366 return;
367 }
368 System.err.print(".");
369 glWindow.display();
370 }
371 });
372 NewtCanvasSWT.create( dsc.composite, 0, glWindow ) ;
373 }
374
375 dsc.display.syncExec( new Runnable() {
376 @Override
377 public void run() {
378 dsc.shell.setText( "NewtCanvasSWT Resize Bug Demo" ) ;
379 dsc.shell.setSize( 400, 450 ) ;
380 dsc.shell.open() ;
381 } } );
382
383 final SWTTestUtil.WaitAction awtRobotWaitAction = new SWTTestUtil.WaitAction(dsc.display, true, TestUtil.TIME_SLICE);
384 final SWTTestUtil.WaitAction generalWaitAction = new SWTTestUtil.WaitAction(dsc.display, true, 10);
385
386 Assert.assertTrue("GLWindow didn't become visible natively!", NewtTestUtil.waitForRealized(glWindow, true, awtRobotWaitAction));
387
388 AWTRobotUtil.requestFocus(robot, glWindow, false);
389 AWTRobotUtil.setMouseToClientLocation(robot, glWindow, 50, 50);
390
391 shallStop = false;
392
393 final ResizeThread resizer;
394 {
395 resizer = new ResizeThread( dsc.shell ) ;
396 resizer.start() ;
397 }
398
399 final KeyfireThread keyfire;
400 {
401 keyfire = new KeyfireThread( robot, dsc.display ) ;
402 keyfire.start() ;
403 }
404
405 {
406 final Thread t = new InterruptSource.Thread(null, new Runnable() {
407 @Override
408 public void run() {
409 try {
410 Thread.sleep(duration);
411 } catch (final InterruptedException e) {}
412 resizer.shallStop = true;
413 keyfire.shallStop = true;
414 try
415 {
416 resizer.join();
417 } catch( final InterruptedException e ) { }
418 try
419 {
420 keyfire.join();
421 } catch( final InterruptedException e ) { }
422 shallStop = true;
423 if( null != dsc.display && !dsc.display.isDisposed() ) {
424 dsc.display.wake();
425 }
426 } } );
427 t.setDaemon(true);
428 t.start();
429 }
430
431 try {
432 while( !shallStop && !dsc.display.isDisposed() ) {
433 generalWaitAction.run();
434 }
435 } catch (final Exception e0) {
436 e0.printStackTrace();
437 Assert.assertTrue("Deadlock @ dispatch: "+e0, false);
438 }
439
440 // canvas is disposed implicit, due to it's disposed listener !
441
442 dsc.dispose();
443 }
444
445 public static void main( final String[] args ) {
446 for(int i=0; i<args.length; i++) {
447 if(args[i].equals("-time")) {
448 duration = MiscUtils.atoi(args[++i], duration);
449 }
450 }
451 System.out.println("durationPerTest: "+duration);
452 org.junit.runner.JUnitCore.main(TestNewtCanvasSWTBug628ResizeDeadlockAWT.class.getName());
453 }
454
455}
static void invokeOnOSTKThread(final boolean blocking, final Runnable runnable)
Runs the specified action in an SWT compatible OS toolkit thread, which is:
static Display createDisplay(final String name)
Create a Display entity.
static Screen createScreen(final Display display, final int index)
Create a Screen entity.
A screen may span multiple MonitorDevices representing their combined virtual size.
Definition: Screen.java:58
static final short VK_0
VK_0 thru VK_9 are the same as UTF16/ASCII '0' thru '9' [0x30 - 0x39].
Definition: KeyEvent.java:553
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
final void addKeyListener(final KeyListener l)
Appends the given com.jogamp.newt.event.KeyListener to the end of the list.
Definition: GLWindow.java:902
static GLWindow create(final GLCapabilitiesImmutable caps)
Creates a new GLWindow attaching a new Window referencing a new default Screen and default Display wi...
Definition: GLWindow.java:169
SWT Canvas containing a NEWT Window using native parenting.
static NewtCanvasSWT create(final Composite parent, final int style, final Window child)
Creates an instance using NewtCanvasSWT(Composite, int, Window) on the SWT thread.
Specifies a set of OpenGL capabilities.
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
static GLProfile get(final AbstractGraphicsDevice device, String profile)
Returns a GLProfile object.
static final String GL2
The desktop OpenGL profile 1.x up to 3.0.
Definition: GLProfile.java:579
static void setMouseToClientLocation(Robot robot, final Object obj, final int x, final int y)
static int newtKeyPress(final int i, final Robot robot, final boolean press, final short newtKeyCode, final int msDelay)
No validation is performed .
static void requestFocus(final Robot robot, final Object obj)
FIXME: AWTRobotUtil Cleanup: Use specific type for argument object.
static void waitForIdle(final Robot robot)
Issuing validateAWTEDTIsAlive() before calling Robot#waitForIdle().
static int atoi(final String str, final int def)
Definition: MiscUtils.java:57
static boolean waitForRealized(final Screen screen, final boolean realized, final Runnable waitAction)
void glOrtho(double left, double right, double bottom, double top, double near_val, double far_val)
void glBegin(int mode)
Entry point to C language function: void {@native glBegin}(GLenum mode) Part of GL_VERSION_1_0
void glVertex2f(float x, float y)
Entry point to C language function: void {@native glVertex2f}(GLfloat x, GLfloat y) Part of GL_VER...
void glEnd()
Entry point to C language function: void {@native glEnd}() Part of GL_VERSION_1_0
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
GL2 getGL2()
Casts this object to the GL2 interface.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
static final int GL_COLOR_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_COLOR_BUFFER_BIT" wit...
Definition: GL.java:390
static final int GL_LINE_SMOOTH
Common in ES1, GL2 and GL3.
Definition: GL.java:1243
static final int GL_ONE_MINUS_SRC_ALPHA
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_ONE_MINUS_SRC_ALPHA" ...
Definition: GL.java:166
void glClearColor(float red, float green, float blue, float alpha)
Entry point to C language function: void {@native glClearColor}(GLfloat red, GLfloat green,...
static final int GL_SRC_ALPHA
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_SRC_ALPHA" with expre...
Definition: GL.java:379
void glEnable(int cap)
Entry point to C language function: void {@native glEnable}(GLenum cap) Part of GL_ES_VERSION_2_0,...
void glBlendFunc(int sfactor, int dfactor)
Entry point to C language function: void {@native glBlendFunc}(GLenum sfactor, GLenum dfactor) Par...
void glClear(int mask)
Entry point to C language function: void {@native glClear}(GLbitfield mask) Part of GL_ES_VERSION_...
void glViewport(int x, int y, int width, int height)
Entry point to C language function: void {@native glViewport}(GLint x, GLint y, GLsizei width,...
static final int GL_BLEND
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_BLEND" with expressio...
Definition: GL.java:704
static final int GL_DEPTH_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_DEPTH_BUFFER_BIT" wit...
Definition: GL.java:738
static final int GL_LINES
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_LINES" with expressio...
Definition: GL.java:430
Subset of OpenGL fixed function pipeline's matrix operations.
static final int GL_PROJECTION
Matrix mode projection.
static final int GL_MODELVIEW
Matrix mode modelview.
void glLoadIdentity()
Load the current matrix with the identity matrix.
void glMatrixMode(int mode)
Sets the current matrix mode.
void glColor4f(float red, float green, float blue, float alpha)