static class TimedCache {
long timeout;
volatile A value; // volatile for peek()
O function;
long set;
Lock lock = lock();
bool keepValueWhileCalculating; // special mode to make peek() always return something
// stats
int stores, fails, hits;
*(double timeoutSeconds, IF0 function) {
this(function, timeoutSeconds);
}
*(double timeoutSeconds, O function) {
this(function, timeoutSeconds);
}
*(IF0 function, double timeoutSeconds) {
this((O) function, timeoutSeconds);
}
*(O function, double timeoutSeconds) {
this(timeoutSeconds);
this.function = function;
}
// 0 = no timeout
*(double timeoutSeconds) {
timeout = toMS(timeoutSeconds);
}
A set(A a) {
lock lock;
++stores;
value = a;
set = now();
ret a;
}
bool has() {
lock lock;
clean();
if (set != 0) {
++hits;
true;
}
++fails;
false;
}
A get() {
lock lock;
if (function != null) ret get(function);
clean();
if (set != 0) ++hits; else ++fails;
ret value;
}
A get(IF0 makerFunction) {
ret get((O) makerFunction);
}
A get(O makerFunction) {
lock lock;
if (keepValueWhileCalculating) {
if (value == null || shouldClean())
set((A) callF(makerFunction));
ret value;
} else {
ret this.has() ? getNoClean() : set((A) callF(makerFunction));
}
}
A getNoClean() {
lock lock; // lock to wait for potentially running calculation
ret value;
}
// clear if timed out
void clean() {
lock lock;
if (shouldClean()) clear();
}
bool shouldClean() {
ret timeout > 0 && now() > set+timeout;
}
// clear now
void clear() {
lock lock;
set = 0;
value = null;
}
S stats() {
ret "Stores: " + stores + ", hits: " + hits + ", fails: " + fails;
}
A peek() {
ret value;
}
}