@djolex999/vir-cli 0.23.1 → 0.23.2
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.
- package/dist/pipeline/lock.js +82 -8
- package/dist/pipeline/lock.js.map +1 -1
- package/package.json +1 -1
package/dist/pipeline/lock.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
// Serializes distiller-calling commands (vir run, vir reconcile): two
|
|
5
5
|
// concurrent pipelines double-spend on the same sessions. A pidfile with
|
|
6
6
|
// stale detection — a lock whose PID is dead (or unreadable) is reclaimed,
|
|
7
7
|
// so a crashed run never wedges the daemon.
|
|
8
|
+
//
|
|
9
|
+
// Creation is atomic (O_EXCL via flag "wx"): of two processes starting at the
|
|
10
|
+
// same moment, exactly one creates the file. A check-then-write let both
|
|
11
|
+
// "acquire" it. Removing a STALE lock needs its own guard: without one, a
|
|
12
|
+
// process that judged the lock stale can delete the fresh lock another
|
|
13
|
+
// process just created in its place (8 racing processes produced 3 winners).
|
|
8
14
|
export const LOCK_PATH = join(homedir(), ".vir", "vir.lock");
|
|
9
15
|
export class LockHeldError extends Error {
|
|
10
16
|
pid;
|
|
@@ -26,21 +32,89 @@ function pidAlive(pid) {
|
|
|
26
32
|
return err.code === "EPERM";
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
// Lock file content, or null once it is gone. An unreadable file reads as ""
|
|
36
|
+
// (garbage), which the caller treats as stale, as before.
|
|
37
|
+
function readLock(lockPath) {
|
|
38
|
+
try {
|
|
39
|
+
return readFileSync(lockPath, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return err.code === "ENOENT" ? null : "";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// A reclaim guard is held for microseconds; one older than this was left by a
|
|
46
|
+
// process that crashed mid-reclaim.
|
|
47
|
+
const GUARD_STALE_MS = 10_000;
|
|
48
|
+
function sleepSync(ms) {
|
|
49
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
50
|
+
}
|
|
51
|
+
// Removes the stale lock if it still holds `stale`, under an exclusive guard
|
|
52
|
+
// so that only one process at a time may delete it. While the stale file
|
|
53
|
+
// exists nobody can create the lock (wx) or release it (not their PID), so
|
|
54
|
+
// the re-read below cannot be invalidated before the unlink. Returns false
|
|
55
|
+
// when another process holds the guard.
|
|
56
|
+
function reclaimStale(lockPath, stale) {
|
|
57
|
+
const guard = `${lockPath}.reclaim`;
|
|
58
|
+
try {
|
|
59
|
+
writeFileSync(guard, String(process.pid), { flag: "wx" });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err.code !== "EEXIST")
|
|
63
|
+
throw err;
|
|
64
|
+
try {
|
|
65
|
+
if (Date.now() - statSync(guard).mtimeMs > GUARD_STALE_MS) {
|
|
66
|
+
unlinkSync(guard);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// released meanwhile
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
if (readLock(lockPath) === stale) {
|
|
76
|
+
try {
|
|
77
|
+
unlinkSync(lockPath);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// not removable; the caller gives up after its rounds
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
32
85
|
try {
|
|
33
|
-
|
|
86
|
+
unlinkSync(guard);
|
|
34
87
|
}
|
|
35
88
|
catch {
|
|
36
|
-
//
|
|
89
|
+
// already gone
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
export function acquireLock(lockPath = LOCK_PATH) {
|
|
95
|
+
// Each round ends with the lock released, a stale lock removed, or the
|
|
96
|
+
// reclaim guard busy (wait 10ms); the next create decides the winner.
|
|
97
|
+
for (let round = 0; round < 50; round++) {
|
|
98
|
+
try {
|
|
99
|
+
writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (err.code !== "EEXIST")
|
|
104
|
+
throw err;
|
|
37
105
|
}
|
|
106
|
+
const content = readLock(lockPath);
|
|
107
|
+
if (content === null)
|
|
108
|
+
continue; // released between our create and read
|
|
109
|
+
const holder = Number.parseInt(content.trim(), 10);
|
|
38
110
|
if (Number.isInteger(holder) && holder > 0 && pidAlive(holder)) {
|
|
39
111
|
throw new LockHeldError(holder);
|
|
40
112
|
}
|
|
41
|
-
|
|
113
|
+
if (!reclaimStale(lockPath, content))
|
|
114
|
+
sleepSync(10);
|
|
42
115
|
}
|
|
43
|
-
|
|
116
|
+
throw new Error(`could not take the vir lock at ${lockPath}: a stale lock could not be ` +
|
|
117
|
+
`removed — delete it if no vir process is running`);
|
|
44
118
|
}
|
|
45
119
|
export function releaseLock(lockPath = LOCK_PATH) {
|
|
46
120
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/pipeline/lock.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/pipeline/lock.ts"],"names":[],"mappings":"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"}
|