Skip to content

Instantly share code, notes, and snippets.

@jsdt
Last active January 4, 2017 19:51
Show Gist options
  • Save jsdt/85f4667f8a6f6e1ff821 to your computer and use it in GitHub Desktop.
Save jsdt/85f4667f8a6f6e1ff821 to your computer and use it in GitHub Desktop.
Creates futures that can be used to throttle access to something
import collection.mutable.Queue
import java.util.concurrent.locks.ReentrantLock
import java.util.Timer
import java.util.Date
import java.util.TimerTask
import concurrent.Promise
import concurrent.Future
object TimerBasedThrottler {
lazy val timer: Timer = new Timer(true)
}
class TimerBasedThrottler(var rate: Int, var duration: Long) {
var counter: Int = rate
var lastRefresh: Long = System.currentTimeMillis()
val queue: Queue[Promise[Unit]] = new Queue()
val lock = new ReentrantLock(true)
def refresh: Unit = {
lock.lock()
try {
counter = rate
lastRefresh = System.currentTimeMillis()
while (counter > 0 && !queue.isEmpty) {
counter -= 1
queue.dequeue.success(())
}
if (!queue.isEmpty) scheduleRefresh
} finally {
lock.unlock()
}
}
def scheduleRefresh: Unit = {
val nextTime = lastRefresh + duration
if (System.currentTimeMillis > nextTime) refresh
else {
TimerBasedThrottler.timer.schedule(new TimerTask {def run() { refresh }}, new Date(nextTime))
}
}
def get: Future[Unit] = {
lock.lock()
try {
counter -= 1
if (counter >= 0) {
if (counter == 0) {
scheduleRefresh
}
Future.successful(())
} else {
val p = Promise[Unit]
queue += p
p.future
}
} finally {
lock.unlock()
}
}
}
@jsdt
Copy link
Author

jsdt commented Dec 29, 2015

get could return a Future before obtaining a lock to avoid blocking the calling thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment