I want to execute a function at no less than a given interval in Go, measured by the start of one invocation to the start of the next invocation. The function itself will vary in its execution time.
If the function runs for longer than that interval, I want to run it again immediately. But if/when it eventually resumes completing in less than a full interval, I want it to immediately resume waiting until the next interval-boundary.
For context, this is a rate-limiter-- the called function could easily spin the CPU but it wouldn't produce additional value, as it's interacting with humans who can't possibly react that fast.
An example for clarity (interval == 20ms
in example):
runtime: 15ms
wait: 5ms
runtime: 25ms
wait: 0ms
runtime: 25ms
wait: 0ms
runtime: 15ms
wait: 5ms <-- this is the important bit
If I use time.Ticker
, I believe additional "ticks" will either queue up in the Ticker.C
channel (if it's buffered) causing it to make a bunch of invocations with no delay when it resumes, or the Ticker's writer will block on writing to the channel and end up resuming with an over-long delay for the first invocation afterit resumes.
Right now I'm doing some math, which is working, but it feels like it might be un-idiomatic:
minDurationBetweenRuns := time.Millisecond * 100
for {
lastRunTime := time.Now()
DO_STUFF_HERE()
durationSinceLastRun := time.Now().Sub(lastRunTime)
if durationSinceLastRun < minDurationBetweenRuns {
sleepTime := minDurationBetweenRuns - durationSinceLastRun
if sleepTime > minDurationBetweenRuns {
sleepTime = minDurationBetweenRuns
}
time.Sleep(sleepTime)
}
}