// only executes one action at once. keeps track of // optimal timestamps though, so actions may pile up sclass FlexibleRateTimer implements AutoCloseable { double hertz; transient Runnable action; transient double timestamp; transient DoLater doLater; transient new Flag started; transient volatile bool disposed; transient long delay; // how much are we currently delayed transient L onFrequencyChanged; transient bool verbose; static double minHertz = 1e-8, maxHertz = 1000; *() {} *(double *hertz) {} *(double *hertz, Runnable *action) {} void start { if (!started.raise()) ret; if (verbose) print("Starting timer with " + hertz + " Hz"); _kaboom(); } synchronized void _kaboom() pcall { if (verbose) print(disposed ? "Timer kaboom (disposed)" : "Timer kaboom"); if (disposed) ret; dispose doLater; if (timestamp == 0) timestamp = sysNow(); timestamp += 1000/getFrequency(); long _timestamp = lround(timestamp); doLater = new DoLater(_timestamp, r { delay = sysNow()-_timestamp; if (verbose) print("Timer action"); pcallF(action); _kaboom(); }); if (verbose) print("Timer scheduled for: " + (_timestamp-sysNow())); doLater.enable(); } void cancel { close(); } public synchronized void close { set disposed; dispose doLater; } void setRunnableAndStart(Runnable action) { this.action = action; start(); } // takes affect _after_ next trigger synchronized void setFrequency(double hertz) { if (disposed) ret; hertz = clamp(hertz, minHertz, maxHertz); if (hertz == this.hertz) ret; this.hertz = hertz; pcallFAll(onFrequencyChanged); } // reschedule next action synchronized void setFrequencyImmediately(double hertz) { if (disposed) ret; hertz = clamp(hertz, minHertz, maxHertz); if (hertz == this.hertz) ret; this.hertz = hertz; if (!started.isUp()) ret; if (verbose) print("Timer setFreq doLater=" + doLater); bool b = doLater.cancel(); print("Timer cancel result: " + b + " (" + this.hertz + " -> " + hertz + ")"); timestamp = now(); pcallFAll(onFrequencyChanged); if (b) _kaboom(); } synchronized double getFrequency() { ret hertz; } long currentDelay() { ret delay; } synchronized void onFrequencyChanged(Runnable r) { onFrequencyChanged = addOrCreate(onFrequencyChanged, r); } }