keysemaphore - Keyed Semaphores Maven Central

Quick Start

To use keysemaphore in an existing SBT project with Scala 2.12, 2.13, or 3.0, add the following dependencies to your build.sbt depending on your needs:

libraryDependencies ++= Seq(
  "io.chrisdavenport" %% "keysemaphore" % "<version>"
)

Example

Quick Imports

import cats.effect._
import io.chrisdavenport.keysemaphore.KeySemaphore
import cats.effect.unsafe.implicits.global

Then we build some operations

// Second Action Can't Get Permit
val action1 = {
  for {
    sem <- KeySemaphore.of[IO, Unit]{_ => 1L}
    first <- sem(()).tryAcquire
    second <- sem(()).tryAcquire
  } yield (first, second)
}
// action1: IO[Tuple2[Boolean, Boolean]] = FlatMap(
//   Map(
//     Delay(
//       cats.effect.IO$$$Lambda$15019/400788471@421581aa,
//       cats.effect.tracing.TracingEvent$StackTrace
//     ),
//     io.chrisdavenport.keysemaphore.KeySemaphore$$$Lambda$15020/1590923540@626fec8,
//     cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   repl.MdocSession$App$$Lambda$15021/817388651@19a6cf92,
//   cats.effect.tracing.TracingEvent$StackTrace
// )

action1.unsafeRunSync()
// res0: Tuple2[Boolean, Boolean] = (true, false)

// Not Affected By Other Keys
val action2 = {
  for {
    sem <- KeySemaphore.of[IO, Int]{(_: Int) => 1L}
    first <- sem(1).tryAcquire
    second <- sem(2).tryAcquire
    third <- sem(1).tryAcquire
  } yield (first, second, third)
}
// action2: IO[Tuple3[Boolean, Boolean, Boolean]] = FlatMap(
//   Map(
//     Delay(
//       cats.effect.IO$$$Lambda$15019/400788471@36e4d7a1,
//       cats.effect.tracing.TracingEvent$StackTrace
//     ),
//     io.chrisdavenport.keysemaphore.KeySemaphore$$$Lambda$15020/1590923540@6fcfc390,
//     cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   repl.MdocSession$App$$Lambda$15061/1151402737@5f5ca80f,
//   cats.effect.tracing.TracingEvent$StackTrace
// )

action2.unsafeRunSync()
// res1: Tuple3[Boolean, Boolean, Boolean] = (true, true, false)

// Releases Based on Keys
// This is space safe, so when the semaphore returns to the
// default it removes it from the internal so memory is not
// leaked per key
val action3 = {
  for {
    sem <- KeySemaphore.of[IO, Int]{(_: Int) => 1L}
    first <- sem(1).tryAcquire
    second <- sem(1).tryAcquire
    _ <- sem(1).release
    third <- sem(1).tryAcquire
  } yield (first, second, third)
}
// action3: IO[Tuple3[Boolean, Boolean, Boolean]] = FlatMap(
//   Map(
//     Delay(
//       cats.effect.IO$$$Lambda$15019/400788471@492e1088,
//       cats.effect.tracing.TracingEvent$StackTrace
//     ),
//     io.chrisdavenport.keysemaphore.KeySemaphore$$$Lambda$15020/1590923540@59e78fd3,
//     cats.effect.tracing.TracingEvent$StackTrace
//   ),
//   repl.MdocSession$App$$Lambda$15066/1050643416@48a5143c,
//   cats.effect.tracing.TracingEvent$StackTrace
// )

action3.unsafeRunSync()
// res2: Tuple3[Boolean, Boolean, Boolean] = (true, false, true)