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
}