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; } }