(maxQueueWaitMs: number = MAX_QUEUE_WAIT_MS)
| 85 | * anymore. |
| 86 | */ |
| 87 | export const acquireBuildSlot = (maxQueueWaitMs: number = MAX_QUEUE_WAIT_MS): BuildSlotHandle => { |
| 88 | const requestedAt = Date.now(); |
| 89 | |
| 90 | if (activeBuilds < MAX_CONCURRENT_BUILDS) { |
| 91 | activeBuilds += 1; |
| 92 | let released = false; |
| 93 | return { |
| 94 | promise: Promise.resolve({ acquired: true, waitMs: 0, timedOut: false }), |
| 95 | cancel: () => { |
| 96 | if (released) return; |
| 97 | released = true; |
| 98 | releaseBuildSlot(); |
| 99 | }, |
| 100 | }; |
| 101 | } |
| 102 | |
| 103 | let resolvePromise!: (value: BuildSlotResult) => void; |
| 104 | const promise = new Promise<BuildSlotResult>((resolve) => { |
| 105 | resolvePromise = resolve; |
| 106 | }); |
| 107 | |
| 108 | const waiter: Waiter = { |
| 109 | state: "queued", |
| 110 | grant: () => { |
| 111 | // Guaranteed "queued" here: `releaseBuildSlot` only reaches a waiter by |
| 112 | // shifting it out of `waitQueue`, and both other transitions |
| 113 | // (timeout, cancel) remove the waiter from the queue in the same tick |
| 114 | // they change its state, so a granted waiter can't have been anything |
| 115 | // else. |
| 116 | waiter.state = "granted"; |
| 117 | activeBuilds += 1; |
| 118 | resolvePromise({ acquired: true, waitMs: Date.now() - requestedAt, timedOut: false }); |
| 119 | }, |
| 120 | }; |
| 121 | waitQueue.push(waiter); |
| 122 | |
| 123 | const timer = setTimeout(() => { |
| 124 | if (waiter.state !== "queued") return; |
| 125 | waiter.state = "timed-out"; |
| 126 | const idx = waitQueue.indexOf(waiter); |
| 127 | if (idx !== -1) waitQueue.splice(idx, 1); |
| 128 | resolvePromise({ acquired: false, waitMs: Date.now() - requestedAt, timedOut: true }); |
| 129 | }, maxQueueWaitMs); |
| 130 | |
| 131 | return { |
| 132 | promise, |
| 133 | cancel: () => { |
| 134 | clearTimeout(timer); |
| 135 | if (waiter.state === "queued") { |
| 136 | waiter.state = "cancelled"; |
| 137 | const idx = waitQueue.indexOf(waiter); |
| 138 | if (idx !== -1) waitQueue.splice(idx, 1); |
| 139 | return; |
| 140 | } |
| 141 | if (waiter.state === "granted") { |
| 142 | // Grant already happened (raced with a concurrent `releaseBuildSlot` |
| 143 | // shifting this waiter off the queue) before this cancel ran, and |
| 144 | // the caller never got to consume the slot — hand it back now |
no test coverage detected