// run = trigger (for convenience) sclass ReliableExecuteAfterChange implements Runnable { Runnable action; IActionScheduler actionScheduler; bool trigger; 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 int cancelTimeOut = 10000; synchronized public void trigger aka run aka get() { if (cancelBeforeTrigger) cancelAndPossiblyWait(); trigger = true; if (!running()) 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 licensed { Thread oldThread; synchronized(this) { var currentInserts = syncGetAndClear(inserts); pcallFAll(currentInserts); if (!trigger) { thread = null; break; } oldThread = getWeakRef(threadBeingCancelled); trigger = false; } if (oldThread != null && oldThread != currentThread()) oldThread.join(cancelTimeOut); pcallF(runnable); } } synchronized void cancel() { if (thread == null) ret; threadBeingCancelled = new WeakReference(thread); cancelAndInterruptThread(thread); thread = null; } 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; } void cleanMeUp { cancel(); } selfType cancelBeforeTrigger() { cancelBeforeTrigger = true; this; } void cancelAndPossiblyWait() { if (waitBetweenCancelAndTrigger) cancel(); } // TODO: trigger technically not necessary, just notify (I guess) void insert(Runnable r) { inserts.add(r); trigger(); } }