// run = trigger (for convenience) sclass ReliableExecuteAfterChange is Runnable, AutoCloseable { Runnable action; IActionScheduler actionScheduler; bool trigger; bool running; ScheduledAction thread; *(Runnable *action, IActionScheduler *actionScheduler) {} *(IActionScheduler *actionScheduler, Runnable *action) {} bool cancelBeforeTrigger; // always cancel running thread and wait for it to end before starting new operation bool waitBetweenCancelAndTrigger; // make sure the old thread is actually ended bool cancelOnClose; int cancelTimeOut = 10000; synchronized public void trigger aka run aka get() { if (trigger) if (cancelBeforeTrigger) cancelAndPossiblyWait(); trigger = true; shouldRun = !running()) starting thread = actionScheduler.start(r _run); } synchronized bool running() { ret thread != null; } // use only if this is the last time you trigger this void triggerAndWait() { trigger(); waitUntilDone(); } void waitUntilDone { while (running()) sleep(1); } private void _run() ctex { while true { synchronized(this) { if (!trigger) ret; running = true; trigger = false; } try { action?!; } finally { synchronized(this) { running = false; } } } close { if (cancelOnClose) cancel(); } void cancel() { ScheduledAction toCancel; synchronized { toCancel = thread; } if (toCancel == null) ret; cancelAndInterruptScheduledAction(toCancel); } void cancelAndWait() { Thread _thread; synchronized { if (thread == null) ret; _thread = thread; threadBeingCancelled = new WeakReference(thread); thread = null; } cancelAndInterruptThread(_thread); } void cancelAndTrigger() { cancelAndPossiblyWait(); trigger(); } synchronized bool triggered() { ret trigger; } selfType cancelBeforeTrigger() { cancelBeforeTrigger = true; this; } void cancelAndPossiblyWait() { if (waitBetweenCancelAndTrigger) cancel(); } }