JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
MainThread.java
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
3 * Copyright (c) 2010 JogAmp Community. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * - Redistribution of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistribution in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Sun Microsystems, Inc. or the names of
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * This software is provided "AS IS," without a warranty of any kind. ALL
21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use
34 * in the design, construction, operation or maintenance of any nuclear
35 * facility.
36 */
37
38package com.jogamp.newt.util;
39
40import java.lang.reflect.Method;
41import java.lang.reflect.InvocationTargetException;
42import java.util.ArrayList;
43import java.util.List;
44
45import com.jogamp.nativewindow.NativeWindowFactory;
46
47import com.jogamp.common.os.Platform;
48import com.jogamp.common.util.InterruptSource;
49import com.jogamp.common.util.PropertyAccess;
50import com.jogamp.common.util.ReflectionUtil;
51
52import jogamp.newt.Debug;
53import jogamp.newt.NEWTJNILibLoader;
54
55/**
56 * NEWT Utility class MainThread<P>
57 *
58 * <p>
59 * FIXME: Update this documentation!
60 * This class just provides a main-thread utility, forking of a main java class
61 * on another thread while being able to continue doing platform specific things
62 * on the main-thread. The latter is essential for eg. MacOSX, where we continue
63 * to run NSApp.run().
64 * </p>
65 *
66 * This class provides a startup singleton <i>main thread</i>,
67 * from which a new thread with the users main class is launched.<br>
68 *
69 * Such behavior is necessary for native windowing toolkits,
70 * where the windowing management must happen on the so called
71 * <i>main thread</i> e.g. for Mac OS X !<br>
72 *
73 * Utilizing this class as a launchpad, now you are able to
74 * use a NEWT multithreaded application with window handling within the different threads,
75 * even on these restricted platforms.<br>
76 *
77 * To support your NEWT Window platform,
78 * you have to pass your <i>main thread</i> actions to {@link #invoke invoke(..)},
79 * have a look at the {@link jogamp.newt.driver.macosx.WindowDriver NEWT Mac OSX Window} driver implementation.<br>
80 * <i>TODO</i>: Some hardcoded dependencies exist in this implementation,
81 * where you have to patch this code or factor it out. <P>
82 *
83 * If your platform is not Mac OS X, but you want to test your code without modifying
84 * this class, you have to set the system property <code>newt.MainThread.force</code> to <code>true</code>.<P>
85 *
86 * The code is compatible with all other platform, which support multithreaded windowing handling.
87 * Since those platforms won't trigger the <i>main thread</i> serialization, the main method
88 * will be simply executed, in case you haven't set <code>newt.MainThread.force</code> to <code>true</code>.<P>
89 *
90 * Test case on Mac OS X (or any other platform):
91 <PRE>
92 java -XstartOnFirstThread com.jogamp.newt.util.MainThread demos.es1.RedSquare -GL2 -GL2 -GL2 -GL2
93 </PRE>
94 * Which starts 4 threads, each with a window and OpenGL rendering.<br>
95 */
96public class MainThread {
97 private static final String MACOSXDisplayClassName = "jogamp.newt.driver.macosx.DisplayDriver";
98 private static final Platform.OSType osType;
99 private static final boolean isMacOSX;
100 private static final ThreadGroup rootThreadGroup;
101
102 /** if true, use the main thread EDT, otherwise AWT's EDT */
103 public static final boolean HINT_USE_MAIN_THREAD;
104
105 static {
107 NEWTJNILibLoader.loadNEWTHead();
108 HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() ||
109 PropertyAccess.getBooleanProperty("newt.MainThread.force", true);
110 osType = Platform.getOSType();
111 isMacOSX = osType == Platform.OSType.MACOS;
112 rootThreadGroup = getRootThreadGroup();
113 }
114
115 public static boolean useMainThread = false;
116
117 protected static final boolean DEBUG = Debug.debug("MainThread");
118
119 private static final MainThread singletonMainThread = new MainThread(); // one singleton MainThread
120
121 private static final ThreadGroup getRootThreadGroup() {
122 ThreadGroup rootGroup = Thread.currentThread( ).getThreadGroup( );
123 ThreadGroup parentGroup;
124 while ( ( parentGroup = rootGroup.getParent() ) != null ) {
125 rootGroup = parentGroup;
126 }
127 return rootGroup;
128 }
129
130 private static final Thread[] getAllThreads(final int[] count) {
131 int tn;
132 Thread[] threads = new Thread[ rootThreadGroup.activeCount() ];
133 while ( ( tn = rootThreadGroup.enumerate( threads, true ) ) == threads.length ) {
134 threads = new Thread[ threads.length * 2 ];
135 }
136 count[0] = tn;
137 return threads;
138 }
139 private static final List<Thread> getNonDaemonThreads() {
140 final List<Thread> res = new ArrayList<Thread>();
141 final int[] tn = { 0 };
142 final Thread[] threads = getAllThreads(tn);
143 for(int i = tn[0] - 1; i >= 0; i--) {
144 final Thread thread = threads[i];
145 try {
146 if(thread.isAlive() && !thread.isDaemon()) {
147 res.add(thread);
148 if(DEBUG) System.err.println("XXX0: "+thread.getName()+", "+thread);
149 }
150 } catch (final Throwable t) {
151 t.printStackTrace();
152 }
153 }
154 return res;
155 }
156 private static final int getNonDaemonThreadCount(final List<Thread> ignoreThreads) {
157 int res = 0;
158 final int[] tn = { 0 };
159 final Thread[] threads = getAllThreads(tn);
160
161 for(int i = tn[0] - 1; i >= 0; i--) {
162 final Thread thread = threads[i];
163 try {
164 if(thread.isAlive() && !thread.isDaemon() && !ignoreThreads.contains(thread)) {
165 res++;
166 if(DEBUG) System.err.println("MainAction.run(): non daemon thread: "+thread);
167 }
168 } catch (final Throwable t) {
169 t.printStackTrace();
170 }
171 }
172 return res;
173 }
174
175 static class UserApp extends InterruptSource.Thread {
176 private final String mainClassNameShort;
177 private final String mainClassName;
178 private final String[] mainClassArgs;
179 private final Method mainClassMain;
180 private List<java.lang.Thread> nonDaemonThreadsAtStart;
181
182 public UserApp(final String mainClassName, final String[] mainClassArgs) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
183 this.mainClassName=mainClassName;
184 this.mainClassArgs=mainClassArgs;
185
186 final Class<?> mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader());
187 if(null==mainClass) {
188 throw new ClassNotFoundException("MainAction couldn't find main class "+mainClassName);
189 }
190 mainClassNameShort = mainClass.getSimpleName();
191 mainClassMain = mainClass.getDeclaredMethod("main", new Class[] { String[].class });
192 mainClassMain.setAccessible(true);
193
194 setName(getName()+"-UserApp-"+mainClassNameShort);
195 setDaemon(false);
196
197 if(DEBUG) System.err.println("MainAction(): instantiated: "+getName()+", is daemon "+isDaemon()+", main-class: "+mainClass.getName());
198 }
199
200 @Override
201 public void run() {
202 nonDaemonThreadsAtStart = getNonDaemonThreads();
203 if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" start, nonDaemonThreadsAtStart "+nonDaemonThreadsAtStart);
204 // start user app ..
205 try {
206 if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" invoke "+mainClassName);
207 mainClassMain.invoke(null, new Object[] { mainClassArgs } );
208 } catch (final InvocationTargetException ite) {
209 ite.getTargetException().printStackTrace();
210 return;
211 } catch (final Throwable t) {
212 t.printStackTrace();
213 return;
214 }
215
216 // wait until no more active non-daemon threads are running
217 {
218 int ndtr;
219 while( 0 < ( ndtr = getNonDaemonThreadCount(nonDaemonThreadsAtStart) ) ) {
220 if(DEBUG) System.err.println("MainAction.run(): post user app, non daemon threads alive: "+ndtr);
221 try {
222 java.lang.Thread.sleep(1000);
223 } catch (final InterruptedException e) {
224 e.printStackTrace();
225 }
226 }
227 if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" user app fin: "+ndtr);
228 }
229
230 if ( useMainThread ) {
231 if(isMacOSX) {
232 try {
233 if(DEBUG) {
234 System.err.println("MainAction.main(): "+java.lang.Thread.currentThread()+" MainAction fin - stopNSApp.0");
235 }
236 ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "stopNSApplication",
237 null, null, MainThread.class.getClassLoader());
238 if(DEBUG) {
239 System.err.println("MainAction.main(): "+java.lang.Thread.currentThread()+" MainAction fin - stopNSApp.X");
240 }
241 } catch (final Exception e) {
242 e.printStackTrace();
243 }
244 } else {
245 if(DEBUG) System.err.println("MainAction.run(): "+java.lang.Thread.currentThread().getName()+" MainAction fin - System.exit(0)");
246 System.exit(0);
247 }
248 }
249 }
250 }
251 private static UserApp mainAction;
252
253 /** Your new java application main entry, which pipelines your application
254 * @throws ClassNotFoundException
255 * @throws NoSuchMethodException
256 * @throws SecurityException */
257 public static void main(final String[] args) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
258 final Thread cur = Thread.currentThread();
259
261
262 if(DEBUG) {
263 System.err.println("MainThread.main(): "+cur.getName()+
264 ", useMainThread "+ useMainThread +
265 ", HINT_USE_MAIN_THREAD "+ HINT_USE_MAIN_THREAD +
266 ", isAWTAvailable " + NativeWindowFactory.isAWTAvailable() + ", ostype "+osType+", isMacOSX "+isMacOSX);
267 }
268
269 if(!useMainThread && !NativeWindowFactory.isAWTAvailable()) {
270 throw new RuntimeException("!USE_MAIN_THREAD and no AWT available");
271 }
272
273 if(args.length==0) {
274 return;
275 }
276
277 final String mainClassName=args[0];
278 final String[] mainClassArgs=new String[args.length-1];
279 if(args.length>1) {
280 System.arraycopy(args, 1, mainClassArgs, 0, args.length-1);
281 }
282
283 mainAction = new UserApp(mainClassName, mainClassArgs);
284
285 if(isMacOSX) {
286 ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton",
287 null, null, MainThread.class.getClassLoader());
288 }
289
290 if ( useMainThread ) {
291 try {
292 cur.setName(cur.getName()+"-MainThread");
293 } catch (final Exception e) {}
294
295 // dispatch user's main thread ..
296 mainAction.start();
297
298 if(isMacOSX) {
299 try {
300 if(DEBUG) {
301 System.err.println("MainThread.main(): "+cur.getName()+"- runNSApp");
302 }
303 ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "runNSApplication",
304 null, null, MainThread.class.getClassLoader());
305 } catch (final Exception e) {
306 e.printStackTrace();
307 }
308 }
309 if(DEBUG) { System.err.println("MainThread - wait until last non daemon thread ends ..."); }
310 } else {
311 // run user's main in this thread
312 mainAction.run();
313 }
314 }
315
316 public static MainThread getSingleton() {
317 return singletonMainThread;
318 }
319
320}
321
322
Provides a pluggable mechanism for arbitrary window toolkits to adapt their components to the NativeW...
static synchronized void initSingleton()
Static one time initialization of this factory.
NEWT Utility class MainThread.
Definition: MainThread.java:96
static final boolean DEBUG
static final boolean HINT_USE_MAIN_THREAD
if true, use the main thread EDT, otherwise AWT's EDT
static void main(final String[] args)
Your new java application main entry, which pipelines your application.
static MainThread getSingleton()