diff options
-rw-r--r-- | src/java/com/jogamp/common/util/WorkerThread.java | 51 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestWorkerThread01.java | 30 |
2 files changed, 57 insertions, 24 deletions
diff --git a/src/java/com/jogamp/common/util/WorkerThread.java b/src/java/com/jogamp/common/util/WorkerThread.java index 3cb36d9..a3a9914 100644 --- a/src/java/com/jogamp/common/util/WorkerThread.java +++ b/src/java/com/jogamp/common/util/WorkerThread.java @@ -29,6 +29,7 @@ package com.jogamp.common.util; import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -51,7 +52,9 @@ public class WorkerThread { private volatile boolean shallPause = true; private volatile boolean shallStop = false; private final Duration minPeriod; - private final boolean useMinPeriod; + private final Duration minDelay; + private final long minDelayMS; + private final boolean useMinimum; private final Callback cbWork; private final Runnable cbInitLocked; private final Runnable cbEndLocked; @@ -62,24 +65,28 @@ public class WorkerThread { /** * Instantiates a new {@link WorkerThread}. * @param minPeriod minimum work-loop-period to throttle execution or {@code null} if unthrottled, see {@link #getSleptDuration()} + * @param minDelay minimum work-loop-delay to throttle execution or {@code null} if unthrottled, see {@link #getSleptDuration()} * @param daemonThread argument for {@link Thread#setDaemon(boolean)} * @param work the actual work {@link Callback} to perform. */ - public WorkerThread(final Duration minPeriod, final boolean daemonThread, final Callback work) { - this(minPeriod, daemonThread, work, null, null); + public WorkerThread(final Duration minPeriod, final Duration minDelay, final boolean daemonThread, final Callback work) { + this(minPeriod, minDelay, daemonThread, work, null, null); } /** * Instantiates a new {@link WorkerThread}. * @param minPeriod minimum work-loop-period to throttle execution or {@code null} if unthrottled, see {@link #getSleptDuration()} + * @param minDelay minimum work-loop-delay to throttle execution or {@code null} if unthrottled, see {@link #getSleptDuration()} * @param daemonThread argument for {@link Thread#setDaemon(boolean)} * @param work the actual work {@link Callback} to perform. * @param init optional initialization {@link Runnable} called at {@link #start()} while locked * @param end optional release {@link Runnable} called at {@link #stop()} while locked */ - public WorkerThread(final Duration minPeriod, final boolean daemonThread, final Callback work, final Runnable init, final Runnable end) { + public WorkerThread(final Duration minPeriod, final Duration minDelay, final boolean daemonThread, final Callback work, final Runnable init, final Runnable end) { this.minPeriod = null != minPeriod ? minPeriod : Duration.ZERO; - this.useMinPeriod = this.minPeriod.toMillis() > 0; + this.minDelay = null != minDelay ? minDelay : Duration.ZERO; + this.minDelayMS = minDelay.toMillis(); + this.useMinimum = this.minPeriod.toMillis() > 0 || this.minDelayMS > 0; this.cbWork = work; this.cbInitLocked = init; this.cbEndLocked = end; @@ -205,9 +212,17 @@ public class WorkerThread { public final Duration getMinPeriod() { return minPeriod; } /** - * Returns the slept {@link Duration} delta of {@link #getMinPeriod()} and consumed {@link Callback#run()} duration. + * Returns enforced minimum work-loop-delay or {@link Duration#ZERO} for none. + * @see #getSleptDuration() + */ + public final Duration getMinDelay() { return minDelay; } + + /** + * Returns the slept {@link Duration} delta of {@link #getMinPeriod()} and consumed {@link Callback#run()} duration, + * which minimum is {@link #getMinDelay()}. * <p> - * Returns {@link Duration#ZERO zero} for {@link Duration#ZERO zero} {@link #getMinPeriod()} or exceeding {@link Callback#run()} duration. + * Returns {@link Duration#ZERO zero} for {@link Duration#ZERO zero} {@link #getMinPeriod()} and {@link #getMinDelay()} or exceeding {@link Callback#run()} duration + * without {@link #getMinDelay()}. * </p> */ public final Duration getSleptDuration() { return sleptDuration; } @@ -217,7 +232,7 @@ public class WorkerThread { synchronized(this) { return "Worker[running "+isRunning+", active "+isActive+", blocked "+isBlocked+ ", shall[pause "+shallPause+", stop "+shallStop+ - "], minPeriod[set "+minPeriod.toMillis()+"ms, sleptDelta "+sleptDuration.toMillis()+ + "], minDelay "+minDelay.toMillis()+"ms, minPeriod[set "+minPeriod.toMillis()+"ms, sleptDelta "+sleptDuration.toMillis()+ "ms], daemon "+isDaemonThread+", thread "+thread+"]"; } } @@ -263,19 +278,26 @@ public class WorkerThread { cbWork.run(); } isBlocked = false; - if( useMinPeriod ) { + if( useMinimum ) { final Instant t1 = Instant.now(); final Duration td = Duration.between(t0, t1); if( minPeriod.compareTo(td) > 0 ) { - final Duration sleepMinPeriodDelta = minPeriod.minus(td); - final long tdMinMS = sleepMinPeriodDelta.toMillis(); - if( tdMinMS > 0 ) { - sleptDuration = sleepMinPeriodDelta; - java.lang.Thread.sleep( tdMinMS ); + final Duration minPeriodDelta = minPeriod.minus(td); + final long minPeriodDeltaMS = minPeriodDelta.toMillis(); + if( minPeriodDeltaMS > 0 ) { + final long minSleepMS = Math.max(minDelayMS, minPeriodDeltaMS); + sleptDuration = Duration.of(minSleepMS, ChronoUnit.MILLIS); + java.lang.Thread.sleep( minSleepMS ); + } else if( minDelayMS > 0 ) { + sleptDuration = minDelay; + java.lang.Thread.sleep( minDelayMS ); } else { sleptDuration = Duration.ZERO; } // java.util.concurrent.locks.LockSupport.parkNanos(tdMin.toNanos()); + } else if( minDelayMS > 0 ) { + sleptDuration = minDelay; + java.lang.Thread.sleep( minDelayMS ); } else { sleptDuration = Duration.ZERO; } @@ -311,5 +333,4 @@ public class WorkerThread { WorkerThread.this.notifyAll(); // wake-up doStop() } } }; - } diff --git a/src/junit/com/jogamp/common/util/TestWorkerThread01.java b/src/junit/com/jogamp/common/util/TestWorkerThread01.java index 9ac7d75..afd41a1 100644 --- a/src/junit/com/jogamp/common/util/TestWorkerThread01.java +++ b/src/junit/com/jogamp/common/util/TestWorkerThread01.java @@ -98,10 +98,11 @@ public class TestWorkerThread01 extends SingletonJunitCase { System.err.println("WT Resume.X: "+wt); } - public void testAction(final long periodMS, final long actionMS) throws IOException, InterruptedException, InvocationTargetException { + public void testAction(final long periodMS, final long minDelayMS, final long actionMS) throws IOException, InterruptedException, InvocationTargetException { final Action action = new Action( 0 < actionMS ? Duration.of(actionMS, ChronoUnit.MILLIS) : Duration.ZERO); - final WorkerThread wt =new WorkerThread(Duration.of(periodMS, ChronoUnit.MILLIS), true /* daemonThread */, action); - final long maxPeriodMS = Math.max(periodMS, actionMS); + final WorkerThread wt =new WorkerThread(Duration.of(periodMS, ChronoUnit.MILLIS), + Duration.of(minDelayMS, ChronoUnit.MILLIS), true /* daemonThread */, action); + final long maxPeriodMS = Math.max(minDelayMS+actionMS, Math.max(periodMS, actionMS)); int counterA = action.counter.get(); checkStopped(wt); start(wt); @@ -110,9 +111,10 @@ public class TestWorkerThread01 extends SingletonJunitCase { { final Duration td = action.td; final Duration wt_slept = wt.getSleptDuration(); + final long minEps = 4; final long actionMS_d = td.minus( wt_slept ).toMillis() - actionMS; - System.err.println("actionMS_d "+actionMS_d+" = td "+td.toMillis()+"ms - wt_slept "+wt_slept.toMillis()+"ms - actionMS "+actionMS+"ms"); - Assert.assertTrue(Math.abs(actionMS_d) < 4); + System.err.println("actionMS_d "+actionMS_d+" = td "+td.toMillis()+"ms - wt_slept "+wt_slept.toMillis()+"ms - actionMS "+actionMS+"ms < minEps "+minEps+"ms"); + Assert.assertTrue(Math.abs(actionMS_d) < minEps); } checkStarted(wt, false /* isPaused */); @@ -149,22 +151,32 @@ public class TestWorkerThread01 extends SingletonJunitCase { @Test public void test01ZeroAction() throws IOException, InterruptedException, InvocationTargetException { - testAction(16 /* periodMS */, 0 /* actionMS*/); + testAction(16 /* periodMS */, 0 /* minDelayMS */, 0 /* actionMS*/); } @Test public void test02MidAction() throws IOException, InterruptedException, InvocationTargetException { - testAction(16 /* periodMS */, 8 /* actionMS*/); + testAction(16 /* periodMS */, 0 /* minDelayMS */, 8 /* actionMS*/); } @Test public void test03HeavyAction() throws IOException, InterruptedException, InvocationTargetException { - testAction(16 /* periodMS */, 20 /* actionMS*/); + testAction(16 /* periodMS */, 0 /* minDelayMS */, 20 /* actionMS*/); } @Test public void test03ZeroMidAction() throws IOException, InterruptedException, InvocationTargetException { - testAction(0 /* periodMS */, 8 /* actionMS*/); + testAction(0 /* periodMS */, 0 /* minDelayMS */, 8 /* actionMS*/); + } + + @Test + public void test04ZeroMinDelayMidAction() throws IOException, InterruptedException, InvocationTargetException { + testAction(0 /* periodMS */, 4 /* minDelayMS */, 8 /* actionMS*/); + } + + @Test + public void test05MinDelayMidAction() throws IOException, InterruptedException, InvocationTargetException { + testAction(8 /* periodMS */, 8 /* minDelayMS */, 8 /* actionMS*/); } public static void main(final String args[]) throws IOException { |