Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docs for Semaphore #2063

Merged
merged 2 commits into from
Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/assets/semaphore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions docs/std/semaphore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
id: semaphore
title: Semaphore
---

![](assets/semaphore.png)

A semaphore has a non-negative number of permits available. Acquiring a permit decrements the current number of permits and releasing a permit increases the current number of permits. An acquire that occurs when there are no permits available results in semantic blocking until a permit becomes available.

```scala
abstract class Semaphore[F[_]] {
def available: F[Long]
def acquire: F[Unit]
def release: F[Unit]
// ... and more
}
```

## Semantic Blocking and Cancellation

Semaphore does what we call "semantic" blocking, meaning that no actual threads are
being blocked while waiting to acquire a permit. Blocking acquires are cancelable.
armanbilge marked this conversation as resolved.
Show resolved Hide resolved

## Shared Resource

When multiple processes try to access a precious resource you might want to constraint the number of accesses. Here is where `Semaphore[F]` is useful.

Three processes are trying to access a shared resource at the same time but only one at a time will be granted access and the next process have to wait until the resource gets available again (availability is one as indicated by the semaphore counter).

`R1`, `R2` & `R3` will request access of the precious resource concurrently so this could be one possible outcome:

```
R1 >> Availability: 1
R2 >> Availability: 1
R2 >> Started | Availability: 0
R3 >> Availability: 0
--------------------------------
R1 >> Started | Availability: 0
R2 >> Done | Availability: 0
--------------------------------
R3 >> Started | Availability: 0
R1 >> Done | Availability: 0
--------------------------------
R3 >> Done | Availability: 1
```

This means when `R1` and `R2` requested the availability it was one and `R2` was faster in getting access to the resource so it started processing. `R3` was the slowest and saw that there was no availability from the beginning.

Once `R2` was done `R1` started processing immediately showing no availability. Once `R1` was done `R3` started processing immediately showing no availability. Finally, `R3` was done showing an availability of one once again.

```scala mdoc:reset:silent
import cats.effect.{IO, Temporal}
import cats.effect.std.{Console, Semaphore}
import cats.implicits._
import cats.effect.syntax.all._

import scala.concurrent.duration._

class PreciousResource[F[_]: Temporal](name: String, s: Semaphore[F])(implicit F: Console[F]) {
def use: F[Unit] =
for {
x <- s.available
_ <- F.println(s"$name >> Availability: $x")
_ <- s.acquire
y <- s.available
_ <- F.println(s"$name >> Started | Availability: $y")
_ <- s.release.delayBy(3.seconds)
z <- s.available
_ <- F.println(s"$name >> Done | Availability: $z")
} yield ()
}

val program: IO[Unit] =
for {
s <- Semaphore[IO](1)
r1 = new PreciousResource[IO]("R1", s)
r2 = new PreciousResource[IO]("R2", s)
r3 = new PreciousResource[IO]("R3", s)
_ <- List(r1.use, r2.use, r3.use).parSequence.void
} yield ()
```
1 change: 1 addition & 0 deletions site-docs/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"std/queue",
"std/ref",
"std/resource",
"std/semaphore",
"std/supervisor"
]
}
Expand Down