diff --git a/.changeset/few-mayflies-speak.md b/.changeset/few-mayflies-speak.md new file mode 100644 index 0000000000..1db7728fde --- /dev/null +++ b/.changeset/few-mayflies-speak.md @@ -0,0 +1,21 @@ +--- +"effect": minor +--- + +add Semaphore.withPermitsIfAvailable + +You can now use `Semaphore.withPermitsIfAvailable` to run an Effect only if the +Semaphore has enough permits available. This is useful when you want to run an +Effect only if you can acquire a permit without blocking. + +It will return an `Option.Some` with the result of the Effect if the permits were +available, or `None` if they were not. + +```ts +import { Effect } from "effect" + +Effect.gen(function* () { + const semaphore = yield* Effect.makeSemaphore(1) + semaphore.withPermitsIfAvailable(1)(Effect.void) +}) +``` diff --git a/packages/effect/src/Effect.ts b/packages/effect/src/Effect.ts index e667425550..f58f064728 100644 --- a/packages/effect/src/Effect.ts +++ b/packages/effect/src/Effect.ts @@ -5374,6 +5374,8 @@ export interface Permit { export interface Semaphore { /** when the given amount of permits are available, run the effect and release the permits when finished */ withPermits(permits: number): (self: Effect) => Effect + /** only if the given permits are available, run the effect and release the permits when finished */ + withPermitsIfAvailable(permits: number): (self: Effect) => Effect, E, R> /** take the given amount of permits, suspending if they are not yet available */ take(permits: number): Effect /** release the given amount of permits, and return the resulting available permits */ diff --git a/packages/effect/src/internal/effect/circular.ts b/packages/effect/src/internal/effect/circular.ts index cff08b7670..a1c790964f 100644 --- a/packages/effect/src/internal/effect/circular.ts +++ b/packages/effect/src/internal/effect/circular.ts @@ -93,6 +93,17 @@ class Semaphore { (permits) => fiberRuntime.ensuring(restore(self), this.release(permits)) ) ) + + readonly withPermitsIfAvailable = (n: number) => (self: Effect.Effect) => + core.uninterruptibleMask((restore) => + core.suspend(() => { + if (this.free < n) { + return effect.succeedNone + } + this.taken += n + return fiberRuntime.ensuring(restore(effect.asSome(self)), this.release(n)) + }) + ) } /** @internal */