Warning: session_start(): open(/var/lib/php/sessions/sess_jlfenskiupkapeaplasqqn5dqa, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
// only executes one action at once. Keeps track of
// optimal timestamps though, so actions may pile up.
// (call pileUp(false) to change.)
// Frequency may be set to 0 or even very close to zero
// (both values effectively stop the timer).
// persists frequency only
// TODO: Events stacked up after coming out of hibernation? (Windows)
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;
settable ISleeper sleeper = defaultSleeper();
settable bool pileUp = true;
// below actualMinHertz, we stop the timer
static double minHertz = 0, actualMinHertz = 1e-8, maxHertz = 1000;
*() {}
*(double *hertz) {}
*(double *hertz, Runnable *action) {}
void start {
if (!started.raise()) ret;
if (verbose) print("Starting timer with " + hertz + " Hz");
vmBus_timerStarted(this);
_kaboom();
}
// This method reschedules the next timer firing
// (i.e. disposes of the internal timer and optionally
// creates a new one).
synchronized void _kaboom() pcall {
if (verbose) print(disposed ? "Timer kaboom (disposed)" : "Timer kaboom");
if (disposed) ret;
dispose doLater;
if (hertz <= actualMinHertz) ret with timestamp = 0;
if (timestamp == 0) timestamp = sysNow();
timestamp += 1000/getFrequency();
if (!pileUp)
timestamp = max(sysNow(), timestamp);
long _timestamp = lround(timestamp);
doLater = new DoLater(_timestamp, r {
delay = sysNow()-_timestamp;
if (verbose) print("Timer action");
pcallF(action);
_kaboom();
});
doLater.sleeper(sleeper);
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 effect _after_ next trigger
void setFrequency(double hertz) {
synchronized(this) {
if (disposed) ret;
hertz = clamp(hertz, minHertz, maxHertz);
if (hertz == this.hertz) ret;
this.hertz = hertz;
}
pcallFAll(onFrequencyChanged);
}
// reschedule next action
void setFrequencyImmediately(double hertz) {
synchronized(this) {
if (disposed) ret;
hertz = clamp(hertz, minHertz, maxHertz);
if (hertz == this.hertz) ret;
double oldHertz = this.hertz;
this.hertz = hertz;
if (!started.isUp()) ret;
if (verbose) print("Timer setFreq doLater=" + doLater);
bool b = doLater == null || doLater.cancel();
if (verbose) print("Timer cancel result: " + b + " (" + oldHertz + " -> " + hertz + ")");
timestamp = sysNow();
if (b) _kaboom();
}
pcallFAll(onFrequencyChanged);
}
synchronized double getFrequency() { ret hertz; }
long currentDelay() { ret delay; }
synchronized void onFrequencyChanged(Runnable r) {
onFrequencyChanged = addOrCreate(onFrequencyChanged, r);
}
void onFrequencyChangedAndNow(Runnable r) {
onFrequencyChanged(runAndReturn(r));
}
}