@internal
| 34 | |
| 35 | /** @internal */ |
| 36 | class Semaphore { |
| 37 | public waiters = new Set<() => void>() |
| 38 | public taken = 0 |
| 39 | |
| 40 | constructor(public permits: number) {} |
| 41 | |
| 42 | get free() { |
| 43 | return this.permits - this.taken |
| 44 | } |
| 45 | |
| 46 | readonly take = (n: number): Effect.Effect<number> => |
| 47 | core.asyncInterrupt<number>((resume) => { |
| 48 | if (this.free < n) { |
| 49 | const observer = () => { |
| 50 | if (this.free < n) return |
| 51 | this.waiters.delete(observer) |
| 52 | resume(core.suspend(() => { |
| 53 | if (this.free < n) return this.take(n) |
| 54 | this.taken += n |
| 55 | return core.succeed(n) |
| 56 | })) |
| 57 | } |
| 58 | this.waiters.add(observer) |
| 59 | return core.sync(() => { |
| 60 | this.waiters.delete(observer) |
| 61 | }) |
| 62 | } |
| 63 | resume(core.suspend(() => { |
| 64 | if (this.free < n) return this.take(n) |
| 65 | this.taken += n |
| 66 | return core.succeed(n) |
| 67 | })) |
| 68 | }) |
| 69 | |
| 70 | updateTakenUnsafe(fiber: Fiber.RuntimeFiber<any, any>, f: (n: number) => number): Effect.Effect<number> { |
| 71 | this.taken = f(this.taken) |
| 72 | if (this.waiters.size > 0) { |
| 73 | fiber.getFiberRef(currentScheduler).scheduleTask( |
| 74 | () => { |
| 75 | const iter = this.waiters.values() |
| 76 | let item = iter.next() |
| 77 | while (item.done === false && this.free > 0) { |
| 78 | item.value() |
| 79 | item = iter.next() |
| 80 | } |
| 81 | }, |
| 82 | fiber.getFiberRef(core.currentSchedulingPriority), |
| 83 | fiber |
| 84 | ) |
| 85 | } |
| 86 | return core.succeed(this.free) |
| 87 | } |
| 88 | |
| 89 | updateTaken(f: (n: number) => number): Effect.Effect<number> { |
| 90 | return core.withFiberRuntime((fiber) => this.updateTakenUnsafe(fiber, f)) |
| 91 | } |
| 92 | |
| 93 | readonly resize = (permits: number) => |
nothing calls this directly
no test coverage detected
searching dependent graphs…