lockorder
Checks contradictory mutex acquisition order and unreleased return paths.
What it detects
Acquire locks consistently and release them on every return path. A missing unlock is reported only when another path demonstrates that the function owns the corresponding release policy.
The full list of precision boundaries, with the fixtures that pin each one, is in the lockorder design notes.
Checks
| Check | Tier | Kind | What it detects |
|---|---|---|---|
missing-release |
core | defect | Reports return paths that leave an owned lock held. |
recursive-acquire |
core | defect | Reports attempts to acquire a lock that is already held. |
contradictory-order |
core | hazard | Reports bounded cycles in mutex acquisition order, with acquisition and helper-call evidence. |
read-lock-write |
core | hazard | Reports writes to an object while only its read lock is held. |
Why this is flagged
Two goroutines that acquire the same locks in different orders can wait on each other forever. Failing to unlock on one return path can similarly block every later caller that needs the lock.
A read lock is shared, so writing to the object it protects while holding only the read lock races with every other reader.
Locks are compared by declaration, such as a field of a type or a package variable, and an ordering cycle is reported only with an acquisition witness for each edge.
How to fix it
Choose one order for acquiring multiple locks and use it everywhere. Arrange
the matching unlock as soon as each lock is acquired, commonly with defer, so
early returns cannot leave it held.
Examples
Flagged code
var first sync.Mutexvar second sync.Mutex
func forward() { first.Lock() defer first.Unlock() second.Lock() defer second.Unlock()}
func reverse() { second.Lock() defer second.Unlock() // gohawk: contradictory lock order: first and second first.Lock() defer first.Unlock()}Accepted code
var third sync.Mutexvar fourth sync.Mutex
func forwardSafely() { third.Lock() defer third.Unlock() fourth.Lock() defer fourth.Unlock()}
func reverseSafely() { third.Lock() defer third.Unlock() fourth.Lock() defer fourth.Unlock()}