Skip to content

Commit 05bad85

Browse files
🧷 test: Retain the Contention Lock Owner Through Garbage Collection (#283)
Co-authored-by: Lia <lia@librechat.ai>
1 parent 836d001 commit 05bad85

1 file changed

Lines changed: 44 additions & 7 deletions

File tree

‎packages/code/src/process-lock.test.ts‎

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,60 @@ test(
1717
const child = spawn(
1818
process.execPath,
1919
[
20+
'--expose-gc',
2021
'--input-type=module',
2122
'-e',
2223
`
24+
import { setTimeout as delay } from 'node:timers/promises';
2325
import { withProcessLock } from ${JSON.stringify(moduleUrl)};
2426
await withProcessLock(${JSON.stringify(path)}, async () => {
25-
process.stdout.write('locked');
26-
await new Promise(() => { setInterval(() => {}, 1000); });
27+
setImmediate(() => {
28+
global.gc();
29+
setImmediate(() => process.send('locked'));
30+
});
31+
// The timer retains its resolver and the lock-owning async frame.
32+
await delay(60_000);
2733
});
2834
`,
2935
],
30-
{ stdio: ['ignore', 'pipe', 'pipe'] }
36+
{ stdio: ['ignore', 'pipe', 'pipe', 'ipc'] }
3137
);
38+
let stdout = '';
39+
let stderr = '';
40+
child.stdout!.setEncoding('utf8').on('data', (chunk: string) => {
41+
stdout = (stdout + chunk).slice(-4096);
42+
});
43+
child.stderr!.setEncoding('utf8').on('data', (chunk: string) => {
44+
stderr = (stderr + chunk).slice(-4096);
45+
});
3246
const closed = once(child, 'close');
3347
t.after(async () => {
3448
child.kill('SIGKILL');
35-
await closed;
36-
await rm(directory, { recursive: true, force: true });
49+
try {
50+
await closed;
51+
} finally {
52+
t.diagnostic(
53+
JSON.stringify({
54+
stdout,
55+
stderr,
56+
exitCode: child.exitCode,
57+
signal: child.signalCode,
58+
})
59+
);
60+
await rm(directory, { recursive: true, force: true });
61+
}
3762
});
38-
await once(child.stdout!, 'data');
63+
await Promise.race([
64+
once(child, 'message', { signal: t.signal }).then(([marker]) => {
65+
assert.equal(marker, 'locked');
66+
t.diagnostic('Lock owner ready: locked (after GC)');
67+
}),
68+
closed.then(([code, signal]) => {
69+
throw new Error(
70+
`Lock owner exited before readiness: ${code}/${signal}`
71+
);
72+
}),
73+
]);
3974
let entered = false;
4075
await assert.rejects(
4176
withProcessLock(
@@ -47,8 +82,10 @@ test(
4782
)
4883
);
4984
assert.equal(entered, false);
85+
assert.equal(child.exitCode, null);
86+
assert.equal(child.signalCode, null);
5087
child.kill('SIGKILL');
51-
await closed;
88+
assert.deepEqual(await closed, [null, 'SIGKILL']);
5289
await withProcessLock(
5390
path,
5491
async () => {

0 commit comments

Comments
 (0)