MCPcopy Create free account
hub / github.com/EvoMap/evolver / _writeLockAtomic

Function _writeLockAtomic

index.js:552–606  ·  view source on GitHub ↗
(lockFile, payload)

Source from the content-addressed store, hash-verified

550} = require('./src/adapters/scripts/_lockPaths');
551
552function _writeLockAtomic(lockFile, payload) {
553 // Round-6 (§19.8): the previous implementation used tmp + rename, which
554 // makes the WRITE atomic but not the OWNERSHIP claim. Two processes
555 // could both rename their own tmp file over the same lockFile (rename
556 // is atomic per call but successive renames overwrite each other), then
557 // each read it back and -- if the second rename happened between the
558 // first process's rename and its read-back -- see the OTHER process's
559 // PID. Each then concludes "I lost the race" and exits, leaving the
560 // lockFile owned by no live process. Symmetrically, two processes can
561 // each see their own PID if the reads happen between their respective
562 // renames, and both conclude they won.
563 //
564 // The proper primitive is link(2): given a unique tmp file, link to the
565 // target path fails atomically with EEXIST if the target already
566 // exists. Only one of N concurrent linkers succeeds.
567 // NOTE(windows): mode 0o700 / 0o600 are silently ignored on Windows.
568 // The lock directory and tmp file will NOT be owner-only on Windows.
569 // Isolation relies solely on the user-profile directory ACLs.
570 const dir = path.dirname(lockFile);
571 try { fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); } catch (_) {}
572 const tmp = lockFile + '.' + process.pid + '.tmp';
573 fs.writeFileSync(tmp, payload, { encoding: 'utf8', mode: 0o600 });
574 // link() requires the target NOT to exist. The caller in the takeover
575 // path has already unlinked the stale lockFile via fs.unlinkSync
576 // (ignoring ENOENT). If a concurrent process beat us to the link, our
577 // linkSync below throws EEXIST -- we surface that to the caller and
578 // clean up our tmp.
579 //
580 // EXDEV: fs.link() fails with EXDEV when tmp and lockFile are on different
581 // volumes (can happen on Windows when EVOLVER_LOCK_DIR points to a drive
582 // other than the tmp dir). Fall back to renameSync, which Node.js handles
583 // cross-device by copying + deleting. rename is not atomic in this path,
584 // so the EEXIST guard is lost, but this is an unusual configuration and
585 // the result is still safe (worst case: two daemons both think they won,
586 // the second write wins, the first will exit on its next tick when it
587 // reads back a foreign PID via the heartbeat).
588 try {
589 fs.linkSync(tmp, lockFile);
590 } catch (err) {
591 if (err && err.code === 'EXDEV') {
592 // Cross-device: rename falls back to copy+delete inside Node.js; this
593 // loses the atomic-EEXIST guarantee but is better than hard-failing.
594 try {
595 fs.renameSync(tmp, lockFile);
596 } catch (renameErr) {
597 try { fs.unlinkSync(tmp); } catch (_) {}
598 throw renameErr;
599 }
600 return; // tmp has been consumed by renameSync, skip unlinkSync below
601 }
602 try { fs.unlinkSync(tmp); } catch (_) {}
603 throw err;
604 }
605 try { fs.unlinkSync(tmp); } catch (_) {}
606}
607
608function _readLockPayload(lockFile) {
609 try {

Callers 1

acquireLockFunction · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected