diff options
author | Sven Gothel <[email protected]> | 2015-09-15 05:12:14 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-09-15 05:12:14 +0200 |
commit | 1c4e2d3ea379fe6578dfb84e10f22729b71b1ae5 (patch) | |
tree | 6bcd646a474bcf0c79043fe2520ccaa51a57eea1 /src | |
parent | 2b97ed6d238a0db1e2cf7bdf15349e90eaaa8dfc (diff) |
Bug 1213: Refine changes .. comments and API
- Use InterruptSource.Thread.create(..),
while reducing InterruptSource.Thread ctors to 3 variants.
- Use InterruptSource.Thread instead of java.lang.Thread where possible
- Use SourcedInterruptedException where possible
- SingletonInstanceServerSocket: start(), stop() and run()
- Persistent-Wait and Cancelable
- Add @since 2.3.2
Diffstat (limited to 'src')
11 files changed, 144 insertions, 76 deletions
diff --git a/src/java/com/jogamp/common/util/FunctionTask.java b/src/java/com/jogamp/common/util/FunctionTask.java index 5a58a34..630ae2f 100644 --- a/src/java/com/jogamp/common/util/FunctionTask.java +++ b/src/java/com/jogamp/common/util/FunctionTask.java @@ -49,30 +49,32 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> { } /** - * Invokes <code>func</code> on a new thread belonging to the given {@link ThreadGroup}. + * Invokes <code>func</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. * <p> * The result can be retrieved via {@link FunctionTask#getResult()}, * using the returned instance. * </p> * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> - * @param threadName the name for the new thread + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>func</code> execution is completed, otherwise returns immediately. * @param func the {@link Function} to execute. * @param args the {@link Function} arguments * @return the newly created and invoked {@link FunctionTask} + * @since 2.3.2 */ public static <U,V> FunctionTask<U,V> invokeOnNewThread(final ThreadGroup tg, final String threadName, final boolean waitUntilDone, final Function<U,V> func, final V... args) { final FunctionTask<U,V> rt; if( !waitUntilDone ) { rt = new FunctionTask<U,V>( func, null, true, System.err ); - final InterruptSource.Thread t = new InterruptSource.Thread(tg, rt, threadName); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); rt.args = args; t.start(); } else { final Object sync = new Object(); rt = new FunctionTask<U,V>( func, sync, true, null ); - final InterruptSource.Thread t = new InterruptSource.Thread(tg, rt, threadName); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); synchronized(sync) { rt.args = args; t.start(); diff --git a/src/java/com/jogamp/common/util/InterruptSource.java b/src/java/com/jogamp/common/util/InterruptSource.java index 1d43961..01fcdb5 100644 --- a/src/java/com/jogamp/common/util/InterruptSource.java +++ b/src/java/com/jogamp/common/util/InterruptSource.java @@ -31,6 +31,7 @@ package com.jogamp.common.util; /** * Interface exposing {@link java.lang.Thread#interrupt()} source, * intended for {@link java.lang.Thread} specializations. + * @since 2.3.2 */ public interface InterruptSource { /** @@ -52,7 +53,7 @@ public interface InterruptSource { public static class Util { /** - * Casts given {@link java.lang.Thread} to {@link InterruptSource}, + * Casts given {@link java.lang.Thread} to {@link InterruptSource} * if applicable, otherwise returns {@code null}. */ public static InterruptSource get(final java.lang.Thread t) { @@ -63,7 +64,7 @@ public interface InterruptSource { } } /** - * Casts current {@link java.lang.Thread} to {@link InterruptSource}, + * Casts current {@link java.lang.Thread} to {@link InterruptSource} * if applicable, otherwise returns {@code null}. */ public static InterruptSource currentThread() { @@ -74,25 +75,49 @@ public interface InterruptSource { /** * {@link java.lang.Thread} specialization implementing {@link InterruptSource} * to track {@link java.lang.Thread#interrupt()} calls. + * @since 2.3.2 */ public static class Thread extends java.lang.Thread implements InterruptSource { volatile Throwable interruptSource = null; volatile int interruptCounter = 0; final Object sync = new Object(); - public Thread(final String name) { - super(name); - } - public Thread(final Runnable target) { - super(target); + /** + * See {@link Thread#Thread(} for details. + */ + public Thread() { + super(); } - public Thread(final Runnable target, final String name) { - super(target, name); + /** + * See {@link Thread#Thread(ThreadGroup, Runnable)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + */ + public Thread(final ThreadGroup tg, final Runnable target) { + super(tg, target); } + /** + * See {@link Thread#Thread(ThreadGroup, Runnable, String)} for details. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, must not be {@code null} + */ public Thread(final ThreadGroup tg, final Runnable target, final String name) { super(tg, target, name); } + /** + * Depending on whether {@code name} is null, either + * {@link #Thread(ThreadGroup, Runnable, String)} or + * {@link #Thread(ThreadGroup, Runnable)} is being utilized. + * @param tg explicit {@link ThreadGroup}, may be {@code null} + * @param target explicit {@link Runnable}, may be {@code null} + * @param name explicit name of thread, may be {@code null} + */ + public static Thread create(final ThreadGroup tg, final Runnable target, final String name) { + return null != name ? new Thread(tg, target, name) : new Thread(tg, target); + } + @Override public final Throwable getInterruptSource(final boolean clear) { synchronized(sync) { diff --git a/src/java/com/jogamp/common/util/RunnableTask.java b/src/java/com/jogamp/common/util/RunnableTask.java index 69d7af2..57809b9 100644 --- a/src/java/com/jogamp/common/util/RunnableTask.java +++ b/src/java/com/jogamp/common/util/RunnableTask.java @@ -55,25 +55,27 @@ public class RunnableTask extends TaskBase { } /** - * Invokes <code>runnable</code> on a new thread belonging to the given {@link ThreadGroup}. + * Invokes <code>runnable</code> on a new {@link InterruptSource.Thread}, + * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details. * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code> + * @param threadName the name for the new thread, maybe <code>null</code> * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately. * @param runnable the {@link Runnable} to execute on the new thread. If <code>waitUntilDone</code> is <code>true</code>, - * the runnable <b>must exist</b>, i.e. not loop forever. - * @param threadName the name for the new thread + * the runnable <b>must exit</b>, i.e. not loop forever. * @return the newly created and invoked {@link RunnableTask} + * @since 2.3.2 */ public static RunnableTask invokeOnNewThread(final ThreadGroup tg, final String threadName, final boolean waitUntilDone, final Runnable runnable) { final RunnableTask rt; if( !waitUntilDone ) { rt = new RunnableTask( runnable, null, true, System.err ); - final InterruptSource.Thread t = new InterruptSource.Thread(tg, rt, threadName); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); t.start(); } else { final Object sync = new Object(); rt = new RunnableTask( runnable, sync, true, null ); - final InterruptSource.Thread t = new InterruptSource.Thread(tg, rt, threadName); + final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName); synchronized(sync) { t.start(); while( rt.isInQueue() ) { diff --git a/src/java/com/jogamp/common/util/SourcedInterruptedException.java b/src/java/com/jogamp/common/util/SourcedInterruptedException.java index 7646652..49dcd86 100644 --- a/src/java/com/jogamp/common/util/SourcedInterruptedException.java +++ b/src/java/com/jogamp/common/util/SourcedInterruptedException.java @@ -39,6 +39,7 @@ import com.jogamp.common.ExceptionUtils.CustomStackTrace; * This exception may be created directly where {@link #getCause()} returns {@code null}, * or by propagating an existing {@link InterruptedException} as returned by {@link #getCause()}. * </p> + * @since 2.3.2 */ @SuppressWarnings("serial") public class SourcedInterruptedException extends InterruptedException implements CustomStackTrace { diff --git a/src/java/com/jogamp/common/util/TaskBase.java b/src/java/com/jogamp/common/util/TaskBase.java index 518aeba..0fe85e4 100644 --- a/src/java/com/jogamp/common/util/TaskBase.java +++ b/src/java/com/jogamp/common/util/TaskBase.java @@ -80,7 +80,10 @@ public abstract class TaskBase implements Runnable { } } - /** Returns the execution thread or {@code null} if not yet {@link #run()}. */ + /** + * Returns the execution thread or {@code null} if not yet {@link #run()}. + * @since 2.3.2 + */ public final Thread getExecutionThread() { return execThread; } @@ -135,7 +138,7 @@ public abstract class TaskBase implements Runnable { /** * @return !{@link #isExecuted()} && !{@link #isFlushed()} */ - public final boolean isInQueue() { return !isExecuted() && !isFlushed(); } + public final boolean isInQueue() { return !isExecuted && !isFlushed; } /** * @return True if executed, otherwise false; diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java index 24f0237..44c7a11 100644 --- a/src/java/com/jogamp/common/util/cache/TempFileCache.java +++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java @@ -35,6 +35,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.InterruptSource; import jogamp.common.Debug; @@ -238,7 +239,7 @@ public class TempFileCache { // Add shutdown hook to cleanup the OutputStream, FileChannel, // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. // We do this so that the locks never get garbage-collected. - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { /* @Override */ @Override public void run() { @@ -265,7 +266,7 @@ public class TempFileCache { } // Start a new Reaper thread to do stuff... - final Thread reaperThread = new Thread() { + final Thread reaperThread = new InterruptSource.Thread() { /* @Override */ @Override public void run() { diff --git a/src/java/jogamp/android/launcher/LauncherTempFileCache.java b/src/java/jogamp/android/launcher/LauncherTempFileCache.java index 6836dd9..b576ed9 100644 --- a/src/java/jogamp/android/launcher/LauncherTempFileCache.java +++ b/src/java/jogamp/android/launcher/LauncherTempFileCache.java @@ -34,6 +34,8 @@ import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import com.jogamp.common.util.InterruptSource; + import android.content.Context; public class LauncherTempFileCache { @@ -223,7 +225,7 @@ public class LauncherTempFileCache { // Add shutdown hook to cleanup the OutputStream, FileChannel, // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files. // We do this so that the locks never get garbage-collected. - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() { /* @Override */ public void run() { // NOTE: we don't really expect that this code will ever @@ -249,7 +251,7 @@ public class LauncherTempFileCache { } // Start a new Reaper thread to do stuff... - final Thread reaperThread = new Thread() { + final Thread reaperThread = new InterruptSource.Thread() { /* @Override */ public void run() { deleteOldTempDirs(); diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java index f86dd33..1286924 100644 --- a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java +++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.AbstractOwnableSynchronizer; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.RecursiveLock; /** @@ -197,7 +198,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock { } catch (final InterruptedException e) { if( !wCur.signaledByUnlock ) { sync.queue.remove(wCur); // O(n) - throw e; // propagate interruption not send by unlock + throw SourcedInterruptedException.wrap(e); // propagate interruption not send by unlock } else if( cur != sync.getOwner() ) { // Issued by unlock, but still locked by other thread // diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java index b1b42c3..8ac73fa 100644 --- a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java +++ b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java @@ -33,10 +33,16 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.InterruptSource; +import com.jogamp.common.util.InterruptedRuntimeException; +import com.jogamp.common.util.SourcedInterruptedException; import com.jogamp.common.util.locks.SingletonInstance; public class SingletonInstanceServerSocket extends SingletonInstance { + private static int serverInstanceCount = 0; private final Server singletonServer; private final String fullName; @@ -139,38 +145,58 @@ public class SingletonInstanceServerSocket extends SingletonInstance { public final boolean start() { if(alive) return true; + final String sname; + synchronized (Server.class) { + serverInstanceCount++; + sname = "SingletonServerSocket"+serverInstanceCount+"-"+fullName; + } synchronized (syncOnStartStop) { - serverThread = new Thread(this); + shallQuit = false; + serverThread = InterruptSource.Thread.create(null, this, sname); serverThread.setDaemon(true); // be a daemon, don't keep the JVM running serverThread.start(); try { - syncOnStartStop.wait(); + while( !alive && !shallQuit ) { + syncOnStartStop.wait(); + } } catch (final InterruptedException ie) { - ie.printStackTrace(); + final InterruptedException ie2 = SourcedInterruptedException.wrap(ie); + shutdown(false); + throw new InterruptedRuntimeException(ie2); } } final boolean ok = isBound(); if(!ok) { - shutdown(); + shutdown(true); } return ok; } public final boolean shutdown() { + return shutdown(true); + } + private final boolean shutdown(final boolean wait) { if(!alive) return true; - synchronized (syncOnStartStop) { - shallQuit = true; - connect(); - try { - syncOnStartStop.wait(); - } catch (final InterruptedException ie) { - ie.printStackTrace(); + try { + synchronized (syncOnStartStop) { + shallQuit = true; + connect(); + if( wait ) { + try { + while( alive ) { + syncOnStartStop.wait(); + } + } catch (final InterruptedException ie) { + throw new InterruptedRuntimeException(ie); + } + } + } + } finally { + if(alive) { + System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); + kill(); } - } - if(alive) { - System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?"); - kill(); } return true; } @@ -185,7 +211,8 @@ public class SingletonInstanceServerSocket extends SingletonInstance { System.err.println(infoPrefix()+" XXX "+getName()+" - Kill @ JVM Shutdown"); } alive = false; - if(null != serverThread) { + shallQuit = false; + if(null != serverThread && serverThread.isAlive() ) { try { serverThread.stop(); } catch(final Throwable t) { } @@ -214,47 +241,49 @@ public class SingletonInstanceServerSocket extends SingletonInstance { @Override public void run() { - { - final Thread currentThread = Thread.currentThread(); - currentThread.setName(currentThread.getName() + " - SISock: "+getName()); - if(DEBUG) { - System.err.println(currentThread.getName()+" - started"); - } + if(DEBUG) { + System.err.println(infoPrefix()+" III - Start"); } - alive = false; - synchronized (syncOnStartStop) { - try { - serverSocket = new ServerSocket(portNumber, 1, localInetAddress); - serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed - alive = true; - } catch (final IOException e) { - System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); - shallQuit = true; - } finally { - syncOnStartStop.notifyAll(); + try { + synchronized (syncOnStartStop) { + try { + serverSocket = new ServerSocket(portNumber, 1, localInetAddress); + serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed + alive = true; + } catch (final IOException e) { + System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage()); + shallQuit = true; + } finally { + syncOnStartStop.notifyAll(); + } } - } - while (!shallQuit) { - try { - final Socket clientSocket = serverSocket.accept(); - clientSocket.close(); - } catch (final IOException ioe) { - System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + while (!shallQuit) { + try { + final Socket clientSocket = serverSocket.accept(); + clientSocket.close(); + } catch (final IOException ioe) { + System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage()); + } } - } - - synchronized (syncOnStartStop) { - try { + } catch(final ThreadDeath td) { + if( DEBUG ) { + ExceptionUtils.dumpThrowable("", td); + } + } finally { + synchronized (syncOnStartStop) { + if(DEBUG) { + System.err.println(infoPrefix()+" III - Stopping: alive "+alive+", shallQuit "+shallQuit+", hasSocket "+(null!=serverSocket)); + } if(null != serverSocket) { - serverSocket.close(); + try { + serverSocket.close(); + } catch (final IOException e) { + System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); + } } - } catch (final IOException e) { - System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage()); - } finally { serverSocket = null; alive = false; - shallQuit = false; syncOnStartStop.notifyAll(); } } diff --git a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java index b018a79..a1a9965 100644 --- a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java +++ b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket00.java @@ -35,10 +35,11 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.junit.util.JunitTracer; import com.jogamp.junit.util.SingletonJunitCase; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestSingletonServerSocket00 { +public class TestSingletonServerSocket00 extends JunitTracer { public static final long SINGLE_INSTANCE_LOCK_TO = SingletonJunitCase.SINGLE_INSTANCE_LOCK_TO; public static final long SINGLE_INSTANCE_LOCK_POLL = 100; // poll every 100ms diff --git a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java index b37e600..c637865 100644 --- a/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java +++ b/src/junit/com/jogamp/common/util/locks/TestSingletonServerSocket01.java @@ -35,10 +35,11 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.junit.util.JunitTracer; import com.jogamp.junit.util.SingletonJunitCase; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestSingletonServerSocket01 { +public class TestSingletonServerSocket01 extends JunitTracer { private static volatile SingletonInstance singletonInstance; @BeforeClass |