Skip to main content
Version: 2.x

Semaphore

A Semaphore datatype which allows synchronization between fibers with the withPermit operation, which safely acquires and releases a permit. Semaphore is based on Ref[A] datatype.

Operations​

For example, a synchronization of asynchronous tasks can be done via acquiring and releasing a semaphore with a given number of permits it can spend. When the acquire operation cannot be performed due to no more available permits in the semaphore, such task is semantically blocked, until the permits value is large enough again:

import java.util.concurrent.TimeUnit
import zio._
import zio.Console._

val task = for {
_ <- printLine("start")
_ <- ZIO.sleep(Duration(2, TimeUnit.SECONDS))
_ <- printLine("end")
} yield ()

val semTask = (sem: Semaphore) => for {
_ <- sem.withPermit(task)
} yield ()

val semTaskSeq = (sem: Semaphore) => (1 to 3).map(_ => semTask(sem))

val program = for {

sem <- Semaphore.make(permits = 1)

seq <- ZIO.succeed(semTaskSeq(sem))

_ <- ZIO.collectAllPar(seq)

} yield ()

As the binary semaphore is a special case of a counting semaphore, we can acquire and release any number of permits:

val semTaskN = (sem: Semaphore) => for {
_ <- sem.withPermits(5)(task)
} yield ()

The guarantee of withPermit (and its corresponding counting version withPermits) is that each acquisition will be followed by the equivalent number of releases, regardless of whether the task succeeds, fails, or is interrupted.