Skip to content

Instantly share code, notes, and snippets.

@monadplus
Created May 15, 2019 21:10
Show Gist options
  • Save monadplus/11ddd0142d2e644d8636291e9f844980 to your computer and use it in GitHub Desktop.
Save monadplus/11ddd0142d2e644d8636291e9f844980 to your computer and use it in GitHub Desktop.
Exercise: implement a concurrent semaphore
abstract class Semaphore[F[_]] {
/**
* Returns the number of permits currently available. Always non-negative.
*
* May be out of date the instant after it is retrieved.
* Use `[[tryAcquire]]` or `[[tryAcquireN]]` if you wish to attempt an
* acquire, returning immediately if the current count is not high enough
* to satisfy the request.
*/
def available: F[Long]
/**
* Obtains a snapshot of the current count. May be negative.
*
* Like [[available]] when permits are available but returns the number of permits
* callers are waiting for when there are no permits available.
*/
def count: F[Long]
/**
* Acquires `n` permits.
*
* The returned effect semantically blocks until all requested permits are
* available. Note that acquires are statisfied in strict FIFO order, so given
* `s: Semaphore[F]` with 2 permits available, an `acquireN(3)` will
* always be satisfied before a later call to `acquireN(1)`.
*
* @param n number of permits to acquire - must be >= 0
*/
def acquireN(n: Long): F[Unit]
/** Acquires a single permit. Alias for `[[acquireN]](1)`. */
def acquire: F[Unit] = acquireN(1)
/**
* Acquires `n` permits now and returns `true`, or returns `false` immediately. Error if `n < 0`.
*
* @param n number of permits to acquire - must be >= 0
*/
def tryAcquireN(n: Long): F[Boolean]
/** Alias for `[[tryAcquireN]](1)`. */
def tryAcquire: F[Boolean] = tryAcquireN(1)
/**
* Releases `n` permits, potentially unblocking up to `n` outstanding acquires.
*
* @param n number of permits to release - must be >= 0
*/
def releaseN(n: Long): F[Unit]
/** Releases a single permit. Alias for `[[releaseN]](1)`. */
def release: F[Unit] = releaseN(1)
/**
* Returns an effect that acquires a permit, runs the supplied effect, and then releases the permit.
*/
def withPermit[A](t: F[A]): F[A]
/**
* Modify the context `F` using natural isomorphism `f` with `g`.
*/
def imapK[G[_]](f: F ~> G, g: G ~> F): Semaphore[G] =
new TransformedSemaphore(this, f, g)
}
private type State[F[_]] = Either[Queue[(Long, Deferred[F, Unit])], Long]
private abstract class AbstractSemaphore[F[_]](state: Ref[F, State[F]])(implicit F: Async[F]) extends Semaphore[F]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment