|
|
@@ -1,11 +1,13 @@
|
|
|
package net.ranides.assira.concurrent;
|
|
|
|
|
|
import lombok.experimental.UtilityClass;
|
|
|
-import net.ranides.assira.functional.special.Cancelable;
|
|
|
+import net.ranides.assira.functional.special.CancelableRunnable;
|
|
|
+import net.ranides.assira.functional.special.CancelableConsumer;
|
|
|
|
|
|
import java.util.Timer;
|
|
|
import java.util.TimerTask;
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
+import java.util.function.Consumer;
|
|
|
|
|
|
/**
|
|
|
* Utility methods scheduling tasks using global instance of java.util.Timer
|
|
|
@@ -94,11 +96,37 @@ public class TaskInvoker {
|
|
|
*
|
|
|
* @param delay delay in ms
|
|
|
* @param action action
|
|
|
- * @return Cancelable
|
|
|
+ * @return CancelableRunnable
|
|
|
*/
|
|
|
- public static Cancelable lazy(long delay, Runnable action) {
|
|
|
- // @todo #87
|
|
|
- return new LazyCancelable(delay, action);
|
|
|
+ public static CancelableRunnable lazy(long delay, Runnable action) {
|
|
|
+ return new LazyRunnable(delay, action);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates object which can be used to execute action after "delay" milliseconds.
|
|
|
+ * If the same action is scheduled more than one time, it will cancel previous calls.
|
|
|
+ * Returned object can be used to execute or cancel action.
|
|
|
+ *
|
|
|
+ * Example usage:
|
|
|
+ * action = lazy(1000, () -> code...)
|
|
|
+ *
|
|
|
+ * action.run()
|
|
|
+ * ...
|
|
|
+ * action.run()
|
|
|
+ *
|
|
|
+ * Effect will be dependent on delay between "run" calls.
|
|
|
+ *
|
|
|
+ * It is guaranteed that "code..." will be executed ~500ms after last "run".
|
|
|
+ *
|
|
|
+ * Other "run" calls can cause execution of code, only if delay between them is bigger than 500ms
|
|
|
+ * If not, "code..." execution will be canceled and just moved 500ms into the future.
|
|
|
+ *
|
|
|
+ * @param delay delay in ms
|
|
|
+ * @param action action
|
|
|
+ * @return CancelableRunnable
|
|
|
+ */
|
|
|
+ public static <T> CancelableConsumer<T> lazy(long delay, Consumer<T> action) {
|
|
|
+ return new LazyConsumer<>(delay, action);
|
|
|
}
|
|
|
|
|
|
private static TimerTask asTimerTask(Runnable action) {
|
|
|
@@ -110,7 +138,7 @@ public class TaskInvoker {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- private static class LazyCancelable implements Cancelable {
|
|
|
+ private static class LazyRunnable implements CancelableRunnable {
|
|
|
|
|
|
private final AtomicReference<TimerTask> task = new AtomicReference<>();
|
|
|
|
|
|
@@ -122,7 +150,7 @@ public class TaskInvoker {
|
|
|
|
|
|
private final Runnable action;
|
|
|
|
|
|
- public LazyCancelable(long delay, Runnable action) {
|
|
|
+ public LazyRunnable(long delay, Runnable action) {
|
|
|
this.delay = delay;
|
|
|
this.action = action;
|
|
|
}
|
|
|
@@ -176,4 +204,70 @@ public class TaskInvoker {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private static class LazyConsumer<T> implements CancelableConsumer<T> {
|
|
|
+
|
|
|
+ private final AtomicReference<TimerTask> task = new AtomicReference<>();
|
|
|
+
|
|
|
+ private volatile boolean finished;
|
|
|
+
|
|
|
+ private volatile boolean canceled;
|
|
|
+
|
|
|
+ private final long delay;
|
|
|
+
|
|
|
+ private final Consumer<T> action;
|
|
|
+
|
|
|
+ public LazyConsumer(long delay, Consumer<T> action) {
|
|
|
+ this.delay = delay;
|
|
|
+ this.action = action;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void accept(T argument) {
|
|
|
+ task.getAndUpdate(prev -> {
|
|
|
+ if(prev != null) {
|
|
|
+ prev.cancel();
|
|
|
+ }
|
|
|
+ this.canceled = false;
|
|
|
+ this.finished = false;
|
|
|
+ return later(delay, () -> call(argument));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void call(T argument) {
|
|
|
+ try {
|
|
|
+ action.accept(argument);
|
|
|
+ } finally {
|
|
|
+ task.set(null);
|
|
|
+ finished = true;
|
|
|
+ canceled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean cancel() {
|
|
|
+ if(this.canceled || this.finished) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ task.getAndUpdate(prev -> {
|
|
|
+ if(prev != null) {
|
|
|
+ prev.cancel();
|
|
|
+ this.canceled = true;
|
|
|
+ this.finished = false;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isDone() {
|
|
|
+ return this.finished;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isCanceled() {
|
|
|
+ return this.canceled;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|