diff options
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/macosx/AppKitEDTUtil.java | 338 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java | 25 |
2 files changed, 362 insertions, 1 deletions
diff --git a/src/newt/classes/jogamp/newt/driver/macosx/AppKitEDTUtil.java b/src/newt/classes/jogamp/newt/driver/macosx/AppKitEDTUtil.java new file mode 100644 index 000000000..2eed9fefb --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/macosx/AppKitEDTUtil.java @@ -0,0 +1,338 @@ +/** + * Copyright 2019 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: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.macosx; + +import com.jogamp.nativewindow.NativeWindowException; + +import jogamp.nativewindow.macosx.OSXUtil; +import jogamp.newt.Debug; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.RunnableTask; +import com.jogamp.newt.util.EDTUtil; + +public class AppKitEDTUtil implements EDTUtil { + public static final boolean DEBUG = Debug.debug("EDT"); + + private final Object edtLock = new Object(); // locking the EDT start/stop state + private /* final */ ThreadGroup threadGroup; + private final String name; + private final Runnable dispatchMessages; + private NEDT nedt = null; + private int start_iter=0; + private static long pollPeriod = EDTUtil.defaultEDTPollPeriod; + + public AppKitEDTUtil(final ThreadGroup tg, final String name, final Runnable dispatchMessages) { + this.threadGroup = tg; + this.name=Thread.currentThread().getName()+"-"+name+"-EDT-"; + this.dispatchMessages=dispatchMessages; + this.nedt = new NEDT(threadGroup, this.name); + this.nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + + @Override + final public long getPollPeriod() { + return pollPeriod; + } + + @Override + final public void setPollPeriod(final long ms) { + pollPeriod = ms; // writing to static field is intended + } + + @Override + public final void start() throws IllegalStateException { + synchronized(edtLock) { + if( nedt.isRunning() ) { + throw new IllegalStateException("EDT still running and not subject to stop. Curr "+Thread.currentThread().getName()+", EDT "+nedt.getName()+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + if(DEBUG) { + System.err.println(Thread.currentThread()+": AppKit-EDT reset - edt: "+nedt); + } + if( nedt.getState() != Thread.State.NEW ) { + if( null != threadGroup && threadGroup.isDestroyed() ) { + // best thing we can do is to use this thread's TG + threadGroup = Thread.currentThread().getThreadGroup(); + } + nedt = new NEDT(threadGroup, name); + nedt.setDaemon(true); // don't stop JVM from shutdown .. + } + startImpl(); + } + if( !nedt.isRunning() ) { + throw new RuntimeException("EDT could not be started: "+nedt); + } + } + + private final void startImpl() { + if(nedt.isAlive()) { + throw new RuntimeException("AppKit-EDT Thread.isAlive(): true, isRunning: "+nedt.isRunning+", shouldStop "+nedt.shouldStop+", edt: "+nedt); + } + start_iter++; + nedt.setName(name+start_iter); + if(DEBUG) { + System.err.println(Thread.currentThread()+": AppKit-EDT START - edt: "+nedt); + } + nedt.start(); + } + + @Override + public final boolean isCurrentThreadEDT() { + return OSXUtil.IsMainThread(); + } + + @Override + public final boolean isCurrentThreadNEDT() { + return nedt == Thread.currentThread(); + } + + @Override + public final boolean isCurrentThreadEDTorNEDT() { + return OSXUtil.IsMainThread() || nedt == Thread.currentThread(); + } + + @Override + public final boolean isRunning() { + return nedt.isRunning() ; + } + + @Override + public final boolean invokeStop(final boolean wait, final Runnable task) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": AppKit-EDT.invokeStop wait "+wait); + ExceptionUtils.dumpStack(System.err); + } + return invokeImpl(wait, task, true /* stop */, false /* provokeError */); + } + + public final boolean invokeAndWaitError(final Runnable task) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": AppKit-EDT.invokeAndWaitError"); + ExceptionUtils.dumpStack(System.err); + } + return invokeImpl(true /* wait */, task, false /* stop */, true /* provokeError */); + } + + @Override + public final boolean invoke(final boolean wait, final Runnable task) { + return invokeImpl(wait, task, false /* stop */, false /* provokeError */); + } + + private final boolean invokeImpl(boolean wait, final Runnable task, final boolean stop, final boolean provokeError) { + final RunnableTask rTask; + final Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( nedt.shouldStop ) { + // drop task .. + System.err.println(Thread.currentThread()+": Warning: AppKit-EDT about (1) to stop, won't enqueue new task: "+nedt); + if(DEBUG) { + ExceptionUtils.dumpStack(System.err); + } + return false; + } + if( isCurrentThreadEDT() ) { + if(null != task) { + task.run(); + } + wait = false; // running in same thread (EDT) -> no wait + rTask = null; + if( stop ) { + nedt.shouldStop = true; + } + } else { + if( !nedt.isRunning ) { + if( null != task ) { + if( stop ) { + System.err.println(Thread.currentThread()+": Warning: AppKit-EDT is about (3) to stop and stopped already, dropping task - "+nedt); + } else { + System.err.println(Thread.currentThread()+": Warning: AppKit-EDT is not running, dropping task. NEDT "+nedt); + } + if(DEBUG) { + ExceptionUtils.dumpStack(System.err); + } + } + return false; + } else if( stop ) { + if( nedt.isRunning ) { + if(DEBUG) { + System.err.println(Thread.currentThread()+": AppKit-EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt+", isRunning "+nedt.isRunning+", shouldStop "+nedt.shouldStop); + } + synchronized(nedt.sync) { + nedt.shouldStop = true; + nedt.sync.notifyAll(); // stop immediate if waiting (poll freq) + } + } + } + + if(null != task) { + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */, + wait ? null : System.err); + OSXUtil.RunOnMainThread(false, false, rTask); + } else { + wait = false; + rTask = null; + } + } + } + if( wait ) { + try { + while( rTask.isInQueue() ) { + rTaskLock.wait(); // free lock, allow execution of rTask + } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + final Throwable throwable = rTask.getThrowable(); + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); + } + } + if(DEBUG) { + if( stop) { + System.err.println(Thread.currentThread()+": AppKit-EDT signal STOP X edt: "+nedt); + } + } + return true; + } + } + + @Override + final public boolean waitUntilIdle() { + final NEDT _nedt; + synchronized(edtLock) { + _nedt = nedt; + } + if( !_nedt.isRunning || _nedt == Thread.currentThread() || OSXUtil.IsMainThread() ) { + return false; + } + OSXUtil.WaitUntilFinish(); + return true; + } + + @Override + final public boolean waitUntilStopped() { + synchronized(edtLock) { + final Thread curT = Thread.currentThread(); + final boolean onApkKitEDT = OSXUtil.IsMainThread(); + if( nedt.isRunning && nedt != curT && !onApkKitEDT ) { + try { + while( nedt.isRunning ) { + edtLock.wait(); + } + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); + } + return true; + } else { + return false; + } + } + } + + class NEDT extends InterruptSource.Thread { + volatile boolean shouldStop = false; + volatile boolean isRunning = false; + Object sync = new Object(); + + public NEDT(final ThreadGroup tg, final String name) { + super(tg, null, name); + } + + final public boolean isRunning() { + return isRunning && !shouldStop; + } + + @Override + final public void start() throws IllegalThreadStateException { + isRunning = true; + super.start(); + } + + /** + * Utilizing locking only on tasks and its execution, + * not for event dispatching. + */ + @Override + final public void run() { + if(DEBUG) { + System.err.println(getName()+": AppKit-EDT run() START "+ getName()); + } + RuntimeException error = null; + try { + do { + // event dispatch + if(!shouldStop) { + dispatchMessages.run(); + } + // wait + synchronized(sync) { + if(!shouldStop) { + try { + sync.wait(pollPeriod); + } catch (final InterruptedException e) { + throw new InterruptedRuntimeException(e); + } + } + } + } while(!shouldStop) ; + } catch (final Throwable t) { + // handle errors .. + shouldStop = true; + if(t instanceof RuntimeException) { + error = (RuntimeException) t; + } else { + error = new RuntimeException("Within AppKit-EDT", t); + } + } finally { + if(DEBUG) { + System.err.println(getName()+": AppKit-EDT run() END "+ getName()+", "+error); + } + synchronized(edtLock) { + isRunning = false; + edtLock.notifyAll(); + } + if(DEBUG) { + System.err.println(getName()+": AppKit-EDT run() EXIT "+ getName()+", exception: "+error); + } + if(null!=error) { + throw error; + } + } // finally + } // run() + } // EventDispatchThread +} + diff --git a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java index 8ff37872b..94001f440 100644 --- a/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java +++ b/src/newt/classes/jogamp/newt/driver/macosx/DisplayDriver.java @@ -44,15 +44,19 @@ import com.jogamp.nativewindow.util.PixelFormat; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.util.EDTUtil; import com.jogamp.opengl.util.PNGPixelRect; +import jogamp.newt.Debug; import jogamp.newt.DisplayImpl; import jogamp.newt.NEWTJNILibLoader; public class DisplayDriver extends DisplayImpl { private static final PNGPixelRect defaultIconData; + private static final boolean USE_APPKIT_EDTUTIL; static { NEWTJNILibLoader.loadNEWT(); @@ -85,9 +89,11 @@ public class DisplayDriver extends DisplayImpl { defaultIconData.getSize().getWidth(), defaultIconData.getSize().getHeight()); } } + Debug.initSingleton(); + USE_APPKIT_EDTUTIL = PropertyAccess.getBooleanProperty("newt.macos.useAppKitEDTUtil", true, false); if(DEBUG) { - System.err.println("MacDisplay.init App and IDs OK "+Thread.currentThread().getName()); + System.err.println("MacDisplay.init App and IDs OK (useAppKitEDTUtil "+USE_APPKIT_EDTUTIL+") - "+Thread.currentThread().getName()); } } @@ -116,6 +122,23 @@ public class DisplayDriver extends DisplayImpl { aDevice.close(); } + protected EDTUtil createEDTUtil() { + if( USE_APPKIT_EDTUTIL ) { + final EDTUtil def; + if(NewtFactory.useEDT()) { + def = new AppKitEDTUtil(Thread.currentThread().getThreadGroup(), "Display-"+getFQName(), dispatchMessagesRunnable); + if(DEBUG) { + System.err.println("Display.createEDTUtil("+getFQName()+"): "+def.getClass().getName()); + } + } else { + def = null; + } + return def; + } else { + return super.createEDTUtil(); + } + } + /** * {@inheritDoc} * <p> |