com.jogamp.newt.util.MainThread Maven / Gradle / Ivy
/*
* Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*/
package com.jogamp.newt.util;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.media.nativewindow.NativeWindowFactory;
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.ReflectionUtil;
import jogamp.newt.Debug;
import jogamp.newt.NEWTJNILibLoader;
/**
* NEWT Utility class MainThread
*
*
* FIXME: Update this documentation!
* This class just provides a main-thread utility, forking of a main java class
* on another thread while being able to continue doing platform specific things
* on the main-thread. The latter is essential for eg. MacOSX, where we continue
* to run NSApp.run().
*
*
* This class provides a startup singleton main thread,
* from which a new thread with the users main class is launched.
*
* Such behavior is necessary for native windowing toolkits,
* where the windowing management must happen on the so called
* main thread e.g. for Mac OS X !
*
* Utilizing this class as a launchpad, now you are able to
* use a NEWT multithreaded application with window handling within the different threads,
* even on these restricted platforms.
*
* To support your NEWT Window platform,
* you have to pass your main thread actions to {@link #invoke invoke(..)},
* have a look at the {@link jogamp.newt.driver.macosx.WindowDriver NEWT Mac OSX Window} driver implementation.
* TODO: Some hardcoded dependencies exist in this implementation,
* where you have to patch this code or factor it out.
*
* If your platform is not Mac OS X, but you want to test your code without modifying
* this class, you have to set the system property newt.MainThread.force
to true
.
*
* The code is compatible with all other platform, which support multithreaded windowing handling.
* Since those platforms won't trigger the main thread serialization, the main method
* will be simply executed, in case you haven't set newt.MainThread.force
to true
.
*
* Test case on Mac OS X (or any other platform):
java -XstartOnFirstThread com.jogamp.newt.util.MainThread demos.es1.RedSquare -GL2 -GL2 -GL2 -GL2
* Which starts 4 threads, each with a window and OpenGL rendering.
*/
public class MainThread {
private static final String MACOSXDisplayClassName = "jogamp.newt.driver.macosx.DisplayDriver";
private static final Platform.OSType osType;
private static final boolean isMacOSX;
private static final ThreadGroup rootThreadGroup;
/** if true, use the main thread EDT, otherwise AWT's EDT */
public static final boolean HINT_USE_MAIN_THREAD;
static {
NativeWindowFactory.initSingleton();
NEWTJNILibLoader.loadNEWT();
HINT_USE_MAIN_THREAD = !NativeWindowFactory.isAWTAvailable() ||
Debug.getBooleanProperty("newt.MainThread.force", true);
osType = Platform.getOSType();
isMacOSX = osType == Platform.OSType.MACOS;
rootThreadGroup = getRootThreadGroup();
}
public static boolean useMainThread = false;
protected static final boolean DEBUG = Debug.debug("MainThread");
private static final MainThread singletonMainThread = new MainThread(); // one singleton MainThread
private static final ThreadGroup getRootThreadGroup() {
ThreadGroup rootGroup = Thread.currentThread( ).getThreadGroup( );
ThreadGroup parentGroup;
while ( ( parentGroup = rootGroup.getParent() ) != null ) {
rootGroup = parentGroup;
}
return rootGroup;
}
private static final Thread[] getAllThreads(int[] count) {
int tn;
Thread[] threads = new Thread[ rootThreadGroup.activeCount() ];
while ( ( tn = rootThreadGroup.enumerate( threads, true ) ) == threads.length ) {
threads = new Thread[ threads.length * 2 ];
}
count[0] = tn;
return threads;
}
private static final List getNonDaemonThreads() {
List res = new ArrayList();
int[] tn = { 0 };
Thread[] threads = getAllThreads(tn);
for(int i = tn[0] - 1; i >= 0; i--) {
final Thread thread = threads[i];
try {
if(thread.isAlive() && !thread.isDaemon()) {
res.add(thread);
if(DEBUG) System.err.println("XXX0: "+thread.getName()+", "+thread);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
return res;
}
private static final int getNonDaemonThreadCount(List ignoreThreads) {
int res = 0;
int[] tn = { 0 };
Thread[] threads = getAllThreads(tn);
for(int i = tn[0] - 1; i >= 0; i--) {
final Thread thread = threads[i];
try {
if(thread.isAlive() && !thread.isDaemon() && !ignoreThreads.contains(thread)) {
res++;
if(DEBUG) System.err.println("MainAction.run(): non daemon thread: "+thread);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
return res;
}
static class UserApp extends Thread {
private final String mainClassNameShort;
private final String mainClassName;
private final String[] mainClassArgs;
private final Method mainClassMain;
private List nonDaemonThreadsAtStart;
public UserApp(String mainClassName, String[] mainClassArgs) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
super();
this.mainClassName=mainClassName;
this.mainClassArgs=mainClassArgs;
final Class> mainClass = ReflectionUtil.getClass(mainClassName, true, getClass().getClassLoader());
if(null==mainClass) {
throw new ClassNotFoundException("MainAction couldn't find main class "+mainClassName);
}
mainClassNameShort = mainClass.getSimpleName();
mainClassMain = mainClass.getDeclaredMethod("main", new Class[] { String[].class });
mainClassMain.setAccessible(true);
setName(getName()+"-UserApp-"+mainClassNameShort);
setDaemon(false);
if(DEBUG) System.err.println("MainAction(): instantiated: "+getName()+", is daemon "+isDaemon()+", main-class: "+mainClass.getName());
}
@Override
public void run() {
nonDaemonThreadsAtStart = getNonDaemonThreads();
if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" start, nonDaemonThreadsAtStart "+nonDaemonThreadsAtStart);
// start user app ..
try {
if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" invoke "+mainClassName);
mainClassMain.invoke(null, new Object[] { mainClassArgs } );
} catch (InvocationTargetException ite) {
ite.getTargetException().printStackTrace();
return;
} catch (Throwable t) {
t.printStackTrace();
return;
}
// wait until no more active non-daemon threads are running
{
int ndtr;
while( 0 < ( ndtr = getNonDaemonThreadCount(nonDaemonThreadsAtStart) ) ) {
if(DEBUG) System.err.println("MainAction.run(): post user app, non daemon threads alive: "+ndtr);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" user app fin: "+ndtr);
}
if ( useMainThread ) {
if(isMacOSX) {
try {
if(DEBUG) {
System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.0");
}
ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "stopNSApplication",
null, null, MainThread.class.getClassLoader());
if(DEBUG) {
System.err.println("MainAction.main(): "+Thread.currentThread()+" MainAction fin - stopNSApp.X");
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
if(DEBUG) System.err.println("MainAction.run(): "+Thread.currentThread().getName()+" MainAction fin - System.exit(0)");
System.exit(0);
}
}
}
}
private static UserApp mainAction;
/** Your new java application main entry, which pipelines your application
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws SecurityException */
public static void main(String[] args) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
final Thread cur = Thread.currentThread();
useMainThread = HINT_USE_MAIN_THREAD;
if(DEBUG) {
System.err.println("MainThread.main(): "+cur.getName()+
", useMainThread "+ useMainThread +
", HINT_USE_MAIN_THREAD "+ HINT_USE_MAIN_THREAD +
", isAWTAvailable " + NativeWindowFactory.isAWTAvailable() + ", ostype "+osType+", isMacOSX "+isMacOSX);
}
if(!useMainThread && !NativeWindowFactory.isAWTAvailable()) {
throw new RuntimeException("!USE_MAIN_THREAD and no AWT available");
}
if(args.length==0) {
return;
}
String mainClassName=args[0];
String[] mainClassArgs=new String[args.length-1];
if(args.length>1) {
System.arraycopy(args, 1, mainClassArgs, 0, args.length-1);
}
mainAction = new UserApp(mainClassName, mainClassArgs);
if(isMacOSX) {
ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "initSingleton",
null, null, MainThread.class.getClassLoader());
}
if ( useMainThread ) {
try {
cur.setName(cur.getName()+"-MainThread");
} catch (Exception e) {}
// dispatch user's main thread ..
mainAction.start();
if(isMacOSX) {
try {
if(DEBUG) {
System.err.println("MainThread.main(): "+cur.getName()+"- runNSApp");
}
ReflectionUtil.callStaticMethod(MACOSXDisplayClassName, "runNSApplication",
null, null, MainThread.class.getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
if(DEBUG) { System.err.println("MainThread - wait until last non daemon thread ends ..."); }
} else {
// run user's main in this thread
mainAction.run();
}
}
public static MainThread getSingleton() {
return singletonMainThread;
}
}