Documentation
¶
Overview ¶
Package gpulock is the READ-ONLY Go view of the single-slot GPU lock owned by the Node render runners (render/gpu-lock.mjs). The 8GB GPU is shared between llama-swap (the VLM/text tiers) and ComfyUI/Chatterbox (the generation jobs); the runners serialize GPU-heavy work through one lock DIRECTORY (mkdir is atomic) holding a meta.json {pid, startedAt}.
LO-1 evidence: while a generation job held that lock, llama-swap could not (re)load the vision model, so EVERY vision call 5xx'd and deferred to the expensive cloud model (295 of the 337 all-time defers landed inside one such hour). This package lets the pipeline SEE the lock before burning a doomed HTTP call: resolve the same path the runners use, report held/not-held plus holder age, and wait (bounded) for the slot to free.
Invariants:
- Read-only: this package NEVER creates, reclaims, or removes the lock — acquisition/stale-reclaim stay with the runners (gpu-lock.mjs owns them).
- Same staleness rule as gpu-lock.mjs isStale(): missing/unreadable meta => stale; a recorded holder pid that is dead => stale; older than the 1h TTL (the no-pid fallback + pid-recycle backstop) => stale. Stale == not held.
Index ¶
Constants ¶
const DefaultLockName = "local-offload-gpu.lock"
DefaultLockName is the basename of the shared lock dir under the OS temp dir, matching defaultLockPath() in render/gpu-lock.mjs.
const DefaultTTL = time.Hour
DefaultTTL mirrors DEFAULT_TTL_MS in render/gpu-lock.mjs (1h — a real video generation can take many minutes; the TTL is only the fallback when the holder pid is unknown, and the backstop against pid recycling).
Variables ¶
This section is empty.
Functions ¶
func Path ¶
Path resolves the shared GPU lock directory exactly the way the Node render runners do (gpu-lock.mjs defaultLockPath): explicit override first (the gpu_lock_path config field — the pipeline also threads it to the runners as the GPU_LOCK env so both sides always contend on ONE path), then the GPU_LOCK env, then <os-tmpdir>/local-offload-gpu.lock. Note the default is TMPDIR- relative (per the .mjs), NOT exe-dir-relative like the render script paths.
Types ¶
type Info ¶
type Info struct {
// Held is true when the lock dir exists AND its meta is not stale.
Held bool
// Age is how long the current holder has held the lock (time since the
// meta.json mtime — the same clock gpu-lock.mjs uses). Zero when not held.
Age time.Duration
// PID is the recorded holder pid (0 when unknown / not held).
PID int
}
Info is one point-in-time inspection of the lock.
func Inspect ¶
Inspect reports whether the lock at lockPath is currently held, using the same staleness rule as gpu-lock.mjs with the default 1h TTL. A stale lock (dead holder / no meta / over-TTL) reports NOT held — reclaiming it is the runners' job, never ours.
func WaitFree ¶
WaitFree polls the lock every poll (min bound 1ms; the pipeline passes 2s) until it is free or wait has elapsed (or ctx is done), returning the FINAL inspection: Held=false means the slot freed (proceed); Held=true means the caller should defer, with Age available for the defer reason.