All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.mklinger.qetcher.client.common.concurrent.Delay Maven / Gradle / Ivy

There is a newer version: 2.0.42.rc
Show newest version
package de.mklinger.qetcher.client.common.concurrent;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * Code taken from OpenJDK 11, GPL + PUBLIC DOMAIN.
 */
public class Delay {
	/** No instantiation. */
	private Delay() {}

	private static final boolean USE_COMMON_POOL =
			(ForkJoinPool.getCommonPoolParallelism() > 1);

	/**
	 * Default executor -- ForkJoinPool.commonPool() unless it cannot
	 * support parallelism.
	 */
	private static final Executor ASYNC_POOL = USE_COMMON_POOL ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

	/** Fallback if ForkJoinPool.commonPool() cannot support parallelism */
	private static final class ThreadPerTaskExecutor implements Executor {
		@Override
		public void execute(final Runnable r) { new Thread(r).start(); }
	}

	/**
	 * Returns a new Executor that submits a task to the default
	 * executor after the given delay (or no delay if non-positive).
	 * Each delay commences upon invocation of the returned executor's
	 * {@code execute} method.
	 *
	 * @param delay how long to delay, in units of {@code unit}
	 * @param unit a {@code TimeUnit} determining how to interpret the
	 *        {@code delay} parameter
	 * @return the new delayed executor
	 * @since 9
	 */
	public static Executor delayedExecutor(final long delay, final TimeUnit unit) {
		if (unit == null) {
			throw new NullPointerException();
		}
		return new DelayedExecutor(delay, unit, ASYNC_POOL);
	}

	/**
	 * Singleton delay scheduler, used only for starting and
	 * cancelling tasks.
	 */
	private static final class Delayer {
		static ScheduledFuture delay(final Runnable command, final long delay,
				final TimeUnit unit) {
			return delayer.schedule(command, delay, unit);
		}

		static final class DaemonThreadFactory implements ThreadFactory {
			@Override
			public Thread newThread(final Runnable r) {
				final Thread t = new Thread(r);
				t.setDaemon(true);
				t.setName("CompletableFutureDelayScheduler");
				return t;
			}
		}

		static final ScheduledThreadPoolExecutor delayer;
		static {
			(delayer = new ScheduledThreadPoolExecutor(
					1, new DaemonThreadFactory())).
			setRemoveOnCancelPolicy(true);
		}
	}

	// Little class-ified lambdas to better support monitoring

	private static final class DelayedExecutor implements Executor {
		final long delay;
		final TimeUnit unit;
		final Executor executor;
		DelayedExecutor(final long delay, final TimeUnit unit, final Executor executor) {
			this.delay = delay; this.unit = unit; this.executor = executor;
		}
		@Override
		public void execute(final Runnable r) {
			Delayer.delay(new TaskSubmitter(executor, r), delay, unit);
		}
	}

	/** Action to submit user task */
	private static final class TaskSubmitter implements Runnable {
		final Executor executor;
		final Runnable action;
		TaskSubmitter(final Executor executor, final Runnable action) {
			this.executor = executor;
			this.action = action;
		}
		@Override
		public void run() { executor.execute(action); }
	}

	// -------------- timeout

	/**
	 * Exceptionally completes the given CompletableFuture with
	 * a {@link TimeoutException} if not otherwise completed
	 * before the given timeout.
	 *
	 * @param timeout how long to wait before completing exceptionally
	 *        with a TimeoutException, in units of {@code unit}
	 * @param unit a {@code TimeUnit} determining how to interpret the
	 *        {@code timeout} parameter
	 */
	public static  CompletableFuture timeout(final CompletableFuture cf, final long timeout, final TimeUnit unit, final Supplier s) {
		if (unit == null) {
			throw new NullPointerException();
		}
		if (!cf.isDone()) {
			cf.whenComplete(new Canceller(Delayer.delay(new Timeout(cf, s),
					timeout, unit)));
		}
		return cf;
	}

	/** Action to cancel unneeded timeouts */
	private static final class Canceller implements BiConsumer {
		final Future f;
		Canceller(final Future f) { this.f = f; }
		@Override
		public void accept(final Object ignore, final Throwable ex) {
			if (ex == null && f != null && !f.isDone()) {
				f.cancel(false);
			}
		}
	}

	/** Action to completeExceptionally on timeout */
	private static final class Timeout implements Runnable {
		final CompletableFuture f;
		final Supplier s;
		Timeout(final CompletableFuture f, final Supplier s) { this.f = f; this.s = s; }
		@Override
		public void run() {
			if (f != null && !f.isDone()) {
				f.completeExceptionally(s.get());
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy