How the Daemon Singleton Lock Prevents Concurrent Daemon Instances in No-Mistakes
The no-mistakes daemon guarantees single-instance execution per NM_HOME by acquiring an OS-level exclusive file lock at startup that automatically releases when the process terminates.
The daemon singleton lock in the kunchenguid/no-mistakes repository ensures exactly one live daemon process can manage a given workspace directory at any time. Unlike fragile PID-file schemes, this mechanism leverages kernel-managed file locks through platform-specific syscalls, eliminating race conditions and stale lock scenarios during crashes.
Core Lock Acquisition Mechanism
The singleton enforcement begins in internal/daemon/lock.go (lines 33‑52) with the acquireSingletonLock function. This function opens the lock file via p.LockFile() and delegates to the platform-specific tryLockFile implementation.
If another process already holds the lock, the call returns ErrSingletonLockHeld immediately. The error includes diagnostic metadata about the current holder, allowing administrators to identify the blocking instance without requiring external process inspection tools.
func startDaemon(p *paths.Paths) error {
lock, err := acquireSingletonLock(p)
if err != nil {
// Another daemon is already running – report and exit
return fmt.Errorf("cannot start daemon: %w", err)
}
// Ensure the lock is released on exit
defer lock.Release()
// … continue with daemon initialization …
return runDaemon()
}
Platform-Specific Implementations
The implementation adapts to Unix and Windows kernel locking semantics while maintaining identical Go interfaces.
Unix Advisory Locks via flock
On Linux and macOS, internal/daemon/lock_unix.go invokes syscall.Flock with the LOCK_EX|LOCK_NB flags. This requests a non-blocking exclusive advisory lock on the open file descriptor.
- The lock is advisory, meaning cooperating processes must explicitly attempt acquisition
- The kernel automatically releases the lock when the file descriptor closes or the process terminates
- No cleanup scripts are required after crashes because the OS cleans up the lock state
Windows Mandatory Locks via LockFileEx
The Windows implementation in internal/daemon/lock_windows.go uses the LockFileEx syscall on a byte range at offset 0xFFFFFFFF. This creates a mandatory lock that the kernel enforces for all processes accessing that file region.
Like the Unix variant, Windows releases this lock automatically when the owning process handle terminates, providing the same self-cleaning guarantee across platforms.
Diagnostic Records and Self-Cleaning Safety
While the safety guarantee relies solely on the OS lock mechanism, internal/daemon/lock.go (lines 53‑62) writes a small JSON diagnostic record containing the PID and StartedAt timestamp to the lock file. This metadata aids debugging when ErrSingletonLockHeld occurs.
Crucially, the singleton property does not depend on this JSON file existing or being parseable. Because the lock lives in kernel memory attached to the file descriptor, a crashed daemon cannot leave a stale lock behind. The next startup attempt will successfully acquire the lock regardless of any leftover file contents.
Integration in the Daemon Lifecycle
The daemon acquires the singleton lock before performing any destructive startup work. Specifically, the lock is held prior to:
- Stale-run recovery operations
- Work-tree cleanup procedures
- IPC socket binding
This ordering prevents race conditions where two concurrent daemon instances might attempt simultaneous cleanup or socket binding. The following pattern demonstrates proper error handling when the singleton lock is already held:
if errors.Is(err, daemon.ErrSingletonLockHeld) {
fmt.Println("A daemon is already running for this NM_HOME.")
// Optionally log the holder information from the error message
}
Summary
- The
acquireSingletonLockfunction ininternal/daemon/lock.gocoordinates singleton enforcement by opening the lock file and attempting platform-specific acquisition. - Unix systems use
flockwithLOCK_EX|LOCK_NBfor advisory locking, while Windows usesLockFileExat offset0xFFFFFFFFfor mandatory locking. - Kernel-managed lifecycle ensures the lock releases automatically on process termination, preventing stale lock scenarios without manual intervention.
- Diagnostic metadata (PID and start time) is stored in the lock file for troubleshooting, but the safety guarantee depends entirely on the OS-level lock.
- Strict acquisition ordering ensures the lock is held before any stateful operations, eliminating race conditions during daemon startup.
Frequently Asked Questions
What prevents the singleton lock from becoming stale if the daemon crashes?
The lock is managed by the operating system kernel, not the application. When the daemon process exits or crashes, the kernel automatically releases the file lock associated with the process's file descriptors. This self-cleaning property applies to both the Unix flock implementation and the Windows LockFileEx implementation, ensuring no manual cleanup is required after unexpected terminations.
How does the daemon handle startup attempts when another instance is already running?
When acquireSingletonLock detects that another process holds the lock, it returns ErrSingletonLockHeld immediately without performing any destructive startup operations. The calling code can check for this specific error using errors.Is and gracefully exit, optionally logging the diagnostic information (PID and start time) embedded in the error message to identify the blocking instance.
Why does no-mistakes use an OS-level lock instead of a PID file?
Traditional PID files create race conditions between checking for file existence and writing the new PID, and they require cleanup mechanisms when the owning process crashes. The daemon singleton lock uses kernel-managed file locks that are atomic at the syscall level and automatically released on process termination, providing stronger guarantees without the fragility of PID-file schemes.
Where is the lock file located relative to the NM_HOME directory?
The lock file resides at the path returned by p.LockFile(), which resolves to daemon.lock within the NM_HOME directory structure. This location ensures that separate workspace directories can each run their own daemon instance, while a single NM_HOME cannot host concurrent daemons due to the exclusive lock on that specific file.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →