MCPcopy Create free account
hub / github.com/garrytan/gstack / tryClaim

Function tryClaim

ios-qa/daemon/src/single-instance.ts:33–112  ·  view source on GitHub ↗
(opts: {
  port: number;
  path?: string;
})

Source from the content-addressed store, hash-verified

31 * fails with ESRCH if not. We DO NOT trust a stale pidfile.
32 */
33export async function tryClaim(opts: {
34 port: number;
35 path?: string;
36}): Promise<
37 | { claimed: true; release: () => Promise<void> }
38 | { claimed: false; existing: PidfileContents }
39> {
40 const path = opts.path ?? defaultPidfilePath();
41 await mkdir(dirname(path), { recursive: true, mode: 0o700 });
42
43 // Check for an existing pidfile.
44 if (existsSync(path)) {
45 try {
46 const raw = await readFile(path, 'utf-8');
47 const existing = JSON.parse(raw) as PidfileContents;
48 if (isAlive(existing.pid)) {
49 return { claimed: false, existing };
50 }
51 // Stale — drop it and continue to claim.
52 await unlink(path).catch(() => {});
53 } catch {
54 // Unparseable pidfile — treat as stale.
55 await unlink(path).catch(() => {});
56 }
57 }
58
59 // Use SYNCHRONOUS open with O_EXCL for atomic exclusion. Bun's async
60 // fs.open(wx) doesn't reliably preserve O_EXCL semantics across concurrent
61 // calls in the same process. Sync openSync goes straight to syscall and is
62 // genuinely atomic.
63 //
64 // Constant 0x800 = O_EXCL on macOS/Linux; combined with O_CREAT (0x200) and
65 // O_WRONLY (0x1) it's the equivalent of 'wx'. The sync API accepts the
66 // string flag form too, but explicit numeric flags are the most defensive.
67 const contents: PidfileContents = {
68 pid: process.pid,
69 port: opts.port,
70 startedAt: Date.now(),
71 };
72 let fd: number;
73 try {
74 fd = openSync(path, 'wx', 0o600);
75 } catch (err: unknown) {
76 const e = err as { code?: string };
77 if (e.code === 'EEXIST') {
78 // Race: another caller won.
79 const raw = await readFile(path, 'utf-8').catch(() => '{}');
80 const existing = JSON.parse(raw || '{}') as PidfileContents;
81 return { claimed: false, existing };
82 }
83 throw err;
84 }
85 try {
86 writeSync(fd, JSON.stringify(contents, null, 2));
87 } finally {
88 closeSync(fd);
89 }
90

Callers 2

startDaemonFunction · 0.90

Calls 4

defaultPidfilePathFunction · 0.85
readFileFunction · 0.85
isAliveFunction · 0.85
cleanupFunction · 0.70

Tested by

no test coverage detected