package geniusweb.protocol; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Object containing function that will be called exactly once, on demand or at * given deadline. * */ public class WillBeCalled { private static volatile ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 2); private final Runnable runnable; private volatile AtomicBoolean done = new AtomicBoolean(false); /** * @param r the runnable that will be run once, either when the user * calls {@link #complete(Void)} or after delayMs, whichever * comes first. * @param delayMs the delay after this will be called anywa (the 'deadline' * in milliseconds. If null, no deadline call will be made * and the user must call {@link #complete(Void)} himself. */ public WillBeCalled(Runnable r, Long delayMs) { this.runnable = r; if (delayMs != null) { executor.schedule(() -> complete(), delayMs, TimeUnit.MILLISECONDS); } } /** * Tells the runnable to be executed. Only the first time the function is * run, subsequent calls do nothing. Failures in runnable are NOT caught * here, as they are probably bugs * * We must avoid synchronized here because users may use synchronized blocks * already. */ public void complete() { if (!done.compareAndSet(false, true)) return; runnable.run(); } }