| 246 | } |
| 247 | |
| 248 | function makeReleaseHandle(lockPath: string, ownerPid: number): AcquireLockResult { |
| 249 | let released = false; |
| 250 | return { |
| 251 | lockPath, |
| 252 | release(): void { |
| 253 | if (released) return; |
| 254 | released = true; |
| 255 | if (!existsSync(lockPath)) return; |
| 256 | const contents = readLockContents(lockPath); |
| 257 | if (contents && contents.pid !== ownerPid) { |
| 258 | // Someone else owns the lock now — don't touch it. |
| 259 | return; |
| 260 | } |
| 261 | try { |
| 262 | unlinkSync(lockPath); |
| 263 | } catch { |
| 264 | // Best-effort: file may have vanished between existsSync and unlinkSync. |
| 265 | } |
| 266 | }, |
| 267 | updatePort(port: number): void { |
| 268 | if (!existsSync(lockPath)) return; |
| 269 | const contents = readLockContents(lockPath); |
| 270 | // Only rewrite our own lock, and only when the port actually changed. |
| 271 | if (!contents || contents.pid !== ownerPid || contents.port === port) return; |
| 272 | try { |
| 273 | writeFileSync(lockPath, JSON.stringify({ ...contents, port })); |
| 274 | } catch { |
| 275 | // Best-effort: a concurrent release/takeover may have removed the file. |
| 276 | } |
| 277 | }, |
| 278 | }; |
| 279 | } |