(cacheDir: string)
| 16 | * Call `releaseEventsLock` to release the lock at the end of the process. |
| 17 | */ |
| 18 | export async function tryAcquireEventsLock(cacheDir: string): Promise<boolean> { |
| 19 | const lockPath = eventsLockPath(cacheDir); |
| 20 | await fs.mkdir(cacheDir, { recursive: true }); |
| 21 | |
| 22 | try { |
| 23 | const raw = await fs.readFile(lockPath, 'utf8'); |
| 24 | const parsed = JSON.parse(raw) as unknown; |
| 25 | const holderPid = |
| 26 | typeof parsed === 'object' && |
| 27 | parsed !== null && |
| 28 | typeof (parsed as { pid?: unknown }).pid === 'number' |
| 29 | ? (parsed as { pid: number }).pid |
| 30 | : NaN; |
| 31 | |
| 32 | if (Number.isInteger(holderPid) && holderPid > 0 && isProcessAlive(holderPid)) { |
| 33 | return false; |
| 34 | } |
| 35 | await fs.unlink(lockPath).catch(() => {}); |
| 36 | } catch (error: unknown) { |
| 37 | const err = error as NodeJS.ErrnoException; |
| 38 | // ENOENT = no such file or directory |
| 39 | if (err.code !== 'ENOENT') { |
| 40 | throw error; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | const payload: EventsLockPayload = { pid: process.pid }; |
| 45 | try { |
| 46 | await fs.writeFile(lockPath, JSON.stringify(payload), { |
| 47 | flag: 'wx', // Fail if file already exists. |
| 48 | encoding: 'utf8', |
| 49 | }); |
| 50 | } catch (error: unknown) { |
| 51 | const err = error as NodeJS.ErrnoException; |
| 52 | // EEXIST = file already exists |
| 53 | if (err.code === 'EEXIST') { |
| 54 | return false; |
| 55 | } |
| 56 | throw error; |
| 57 | } |
| 58 | return true; |
| 59 | } |
| 60 | |
| 61 | function eventsLockPath(cacheDir: string): string { |
| 62 | return path.join(cacheDir, EVENTS_LOCK_FILE); |
no test coverage detected