@lmzhen/dsh-evolution-core 0.1.0-rc.65 → 0.1.0-rc.66

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.
Files changed (2) hide show
  1. package/lib/index.js +21 -9
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -52,13 +52,22 @@ function nodeEvolutionIo() {
52
52
  const code = error?.code;
53
53
  return code === "ENOENT" || code === "ENOTDIR";
54
54
  };
55
+ /** True when the pid is alive (EPERM = alive but unowned; ESRCH = gone). */
56
+ const isAlive = (pid) => {
57
+ try {
58
+ process.kill(pid, 0);
59
+ return true;
60
+ } catch (error) {
61
+ return error?.code === "EPERM";
62
+ }
63
+ };
55
64
  /**
56
65
  * Cross-process write lock (claw `withFileLock` parity): an O_EXCL lock file
57
- * guards the atomic write; a >5s-old lock is treated as stale and taken
58
- * over. A held lock that outlasts the retry budget fails LOUD instead of
59
- * proceeding unlocked — two unlocked writers overlap their RMW and lose
60
- * updates, which is exactly how the rc.65 CI-only 7≠8 occurred (a slow
61
- * writer held the lock past the budget and a peer entered unlocked).
66
+ * guards the atomic write. A >5s-old lock is taken over ONLY after probing
67
+ * the holder pid it carries (rc.66): a LIVE holder is never stolen, so a
68
+ * slow writer no longer loses its lock to a peer at the 5s mark (the
69
+ * takeover is the only best-effort surface; the retry budget fails loud —
70
+ * rc.65 — instead of ever proceeding unlocked).
62
71
  */
63
72
  const withWriteLock = async (path, task) => {
64
73
  const lock = `${path}.lock`;
@@ -74,10 +83,13 @@ function nodeEvolutionIo() {
74
83
  try {
75
84
  const st = await stat(lock);
76
85
  if (Date.now() - st.mtimeMs > 5e3) {
77
- try {
78
- await rm(lock, { force: true });
79
- } catch {}
80
- continue;
86
+ const holder = Number(await readFile(lock, "utf8").catch(() => ""));
87
+ if (!(Number.isInteger(holder) && holder > 0 && isAlive(holder))) {
88
+ try {
89
+ await rm(lock, { force: true });
90
+ } catch {}
91
+ continue;
92
+ }
81
93
  }
82
94
  } catch {
83
95
  continue;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.1.0-rc.65",
4
+ "version": "0.1.0-rc.66",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },