| 146 | } |
| 147 | |
| 148 | async function tryAcquireLockDir(lockDir: string, opts: Opts): Promise<Owned | { acquired: false }> { |
| 149 | const token = randomUUID?.() ?? randomBytes(16).toString("hex") |
| 150 | const metaPath = path.join(lockDir, "meta.json") |
| 151 | const heartbeatPath = path.join(lockDir, "heartbeat") |
| 152 | |
| 153 | try { |
| 154 | await mkdir(lockDir, { mode: 0o700 }) |
| 155 | } catch (err) { |
| 156 | if (code(err) !== "EEXIST") { |
| 157 | throw err |
| 158 | } |
| 159 | |
| 160 | if (!(await stale(lockDir, heartbeatPath, metaPath, opts.staleMs))) { |
| 161 | return { acquired: false } |
| 162 | } |
| 163 | |
| 164 | const breakerPath = lockDir + ".breaker" |
| 165 | try { |
| 166 | await mkdir(breakerPath, { mode: 0o700 }) |
| 167 | } catch (claimErr) { |
| 168 | const errCode = code(claimErr) |
| 169 | if (errCode === "EEXIST") { |
| 170 | const breaker = await stats(breakerPath) |
| 171 | if (breaker && wall() - breaker.mtimeMs > opts.staleMs) { |
| 172 | await rm(breakerPath, { recursive: true, force: true }).catch(() => undefined) |
| 173 | } |
| 174 | return { acquired: false } |
| 175 | } |
| 176 | |
| 177 | if (errCode === "ENOENT" || errCode === "ENOTDIR") { |
| 178 | return { acquired: false } |
| 179 | } |
| 180 | |
| 181 | throw claimErr |
| 182 | } |
| 183 | |
| 184 | try { |
| 185 | // Breaker ownership ensures only one contender performs stale cleanup. |
| 186 | if (!(await stale(lockDir, heartbeatPath, metaPath, opts.staleMs))) { |
| 187 | return { acquired: false } |
| 188 | } |
| 189 | |
| 190 | await rm(lockDir, { recursive: true, force: true }) |
| 191 | |
| 192 | try { |
| 193 | await mkdir(lockDir, { mode: 0o700 }) |
| 194 | } catch (retryErr) { |
| 195 | const errCode = code(retryErr) |
| 196 | if (errCode === "EEXIST" || errCode === "ENOTEMPTY") { |
| 197 | return { acquired: false } |
| 198 | } |
| 199 | throw retryErr |
| 200 | } |
| 201 | } finally { |
| 202 | await rm(breakerPath, { recursive: true, force: true }).catch(() => undefined) |
| 203 | } |
| 204 | } |
| 205 | |