import java.util.concurrent.atomic.AtomicInteger import scala.annotation.tailrec final case class SharedThrottle(maxConcurrents: Int) { require(maxConcurrents > 0, s"SharedThrottle.maxConcurrents must be greater than 0 but was $maxConcurrents") private[this] val counter = new AtomicInteger(0) /** * Try to grab an available slot, uses a CAS operation internally to avoid locking, * will self-recurse until this succeeds * @return true if a slot was available and we acquired it, false if not slots free */ @tailrec def seizeSlot(): Boolean = { val n = counter.get @inline def plusone = n + 1 n < maxConcurrents && (counter.compareAndSet(n, plusone) || seizeSlot()) } def releaseSlot(): Unit = { val _ = counter.decrementAndGet() } def slotsUsed: Int = counter.get }