@nowcrew/daemon 0.6.107-beta.1 → 0.6.107-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -311,6 +311,19 @@ export async function acquireProfileSaveLock(profilesDirectory, options = {}) {
|
|
|
311
311
|
catch (error) {
|
|
312
312
|
if (installed)
|
|
313
313
|
throw error;
|
|
314
|
+
if (codeOf(error) === "EINVAL") {
|
|
315
|
+
// A competing release can remove the destination parent during rename.
|
|
316
|
+
// Retry only that observed race; preserve genuine invalid-rename errors.
|
|
317
|
+
const parentMissing = await fileSystem.stat(lockDirectory).then(() => false, (inspectionError) => {
|
|
318
|
+
if (codeOf(inspectionError) === "ENOENT")
|
|
319
|
+
return true;
|
|
320
|
+
throw inspectionError;
|
|
321
|
+
});
|
|
322
|
+
if (parentMissing) {
|
|
323
|
+
await pause();
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
314
327
|
if (codeOf(error) === "ENOENT") {
|
|
315
328
|
await pause();
|
|
316
329
|
continue;
|
|
@@ -3,15 +3,28 @@ import { setTimeout as delay } from "node:timers/promises";
|
|
|
3
3
|
import { MACHINE_ENVIRONMENT_MAX_BYTES, MachineEnvironmentCaptureError } from "./machine-environment-contract.js";
|
|
4
4
|
/** 只接收本模块 detached spawn 所创建的进程组,绝不复用 supervisor 的 PID 清理接口。 */
|
|
5
5
|
async function cleanupOwnedGroup(pid) {
|
|
6
|
-
const exists = () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const exists = async () => {
|
|
7
|
+
const until = Date.now() + 1_000;
|
|
8
|
+
let inspectionDenied = false;
|
|
9
|
+
for (;;) {
|
|
10
|
+
try {
|
|
11
|
+
process.kill(-pid, 0);
|
|
12
|
+
if (!inspectionDenied)
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const code = error.code;
|
|
17
|
+
if (code === "ESRCH")
|
|
18
|
+
return false;
|
|
19
|
+
if (code !== "EPERM")
|
|
20
|
+
throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
|
|
21
|
+
inspectionDenied = true;
|
|
22
|
+
}
|
|
23
|
+
// After denied inspection, wait only for absence. Never signal a group
|
|
24
|
+
// whose identity may have changed, even if it becomes queryable again.
|
|
25
|
+
if (Date.now() >= until)
|
|
26
|
+
throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
|
|
27
|
+
await delay(20);
|
|
15
28
|
}
|
|
16
29
|
};
|
|
17
30
|
const signal = (value) => {
|
|
@@ -23,14 +36,14 @@ async function cleanupOwnedGroup(pid) {
|
|
|
23
36
|
throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
|
|
24
37
|
}
|
|
25
38
|
};
|
|
26
|
-
if (!exists())
|
|
39
|
+
if (!await exists())
|
|
27
40
|
return;
|
|
28
41
|
for (const value of ["SIGTERM", "SIGKILL"]) {
|
|
29
42
|
signal(value);
|
|
30
43
|
const until = Date.now() + 1_000;
|
|
31
|
-
while (
|
|
44
|
+
while (Date.now() < until && await exists())
|
|
32
45
|
await delay(20);
|
|
33
|
-
if (!exists())
|
|
46
|
+
if (!await exists())
|
|
34
47
|
return;
|
|
35
48
|
}
|
|
36
49
|
throw new MachineEnvironmentCaptureError("shell_cleanup_failed");
|