← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Mutex & Locking

Concurrency Intermediate
debt(d9/e5/b5/t7)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). The detection_hints note automated=no and only a regex pattern for flock(). None of the misuses — failing to release in a finally block, using flock() as a distributed lock, or holding locks too long — produce compile-time or lint-time errors. Race conditions and lock leaks are silent until users encounter data corruption or hangs under load in production.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix lists three distinct remediation actions: wrapping releases in finally blocks, replacing flock() with Redis SET NX EX for distributed scenarios, and setting lock timeouts. The flock()-to-Redis migration in particular requires introducing a new dependency, updating configuration, and changing lock acquisition/release logic across all locking sites — more than a single-line swap but not necessarily a full architectural rework.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Locking applies across web, cli, and queue-worker contexts, meaning any shared-state operation in all three runtime environments must account for the locking strategy. A wrong choice (e.g., flock() in a multi-server environment) silently misbehaves everywhere, and the correct distributed-lock pattern must be consistently applied. It is not quite load-bearing across the entire system shape (b7) but imposes meaningful ongoing discipline across many work streams.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The canonical misconception is explicit: developers familiar with mutex/locking concepts from single-server or OS contexts reasonably expect flock() to provide mutual exclusion across a PHP-FPM cluster, but it is per-server only. This directly contradicts reasonable expectations drawn from the concept's name and from how similar primitives work in other languages/environments, making it a serious trap rather than merely a documented gotcha.

About DEBT scoring →

TL;DR

A mutex (mutual exclusion lock) ensures only one thread/process can access a critical section at a time — the fundamental primitive for preventing race conditions.

Explanation

Mutex: binary lock — locked or unlocked. Only the holder can unlock it. Critical section: code protected by the mutex. In PHP: flock() for file-based mutex, Redis SET NX EX for distributed mutex, database advisory locks (GET_LOCK in MySQL). Key properties: atomicity of lock/unlock, single-owner, blocking (waits) vs non-blocking (returns false if unavailable). Distributed systems need distributed locks (Redis Redlock, ZooKeeper, DB advisory locks) since file locks only work within one server. Deadlock risk: never hold mutex A while acquiring mutex B (or maintain consistent order). Release in finally to ensure unlock even on exception.

Common Misconception

flock() provides a distributed lock across multiple servers — flock() is per-server only. Multiple PHP-FPM servers need Redis or DB-based distributed locking.

Why It Matters

Mutexes are the foundation of concurrent programming correctness — without them, any shared mutable state is a potential race condition.

Common Mistakes

  • Not releasing mutex in a finally block — lock held forever on exception.
  • Using flock() as a distributed lock across multiple servers — doesn't work.
  • Holding a mutex too long — reduces concurrency, increases contention.

Code Examples

✗ Vulnerable
$fp = fopen('lock.txt', 'c');
flock($fp, LOCK_EX);
doWork(); // Exception here leaves lock held forever!
flock($fp, LOCK_UN);
fclose($fp);
✓ Fixed
// Always release in finally:
$fp = fopen('lock.txt', 'c');
try {
    flock($fp, LOCK_EX);
    doWork();
} finally {
    flock($fp, LOCK_UN);
    fclose($fp);
}

// Distributed mutex via Redis:
$acquired = $redis->set('lock:job', 1, ['NX', 'EX' => 30]);
if (!$acquired) return; // Another process holds it
try { doWork(); } finally { $redis->del('lock:job'); }

Added 23 Mar 2026
Views 91
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 2 pings S 6 pings S 6 pings M 0 pings T 0 pings W 0 pings T 1 ping F 3 pings S 0 pings S 0 pings M 1 ping T 1 ping W 0 pings T 1 ping F 0 pings S 2 pings S 0 pings M 2 pings T 0 pings W
No pings yet today
PetalBot 1 Perplexity 1
Perplexity 15 Amazonbot 14 Scrapy 14 Google 7 Unknown AI 5 SEMrush 5 Ahrefs 4 ChatGPT 3 Bing 3 Claude 2 Majestic 1 Meta AI 1 PetalBot 1
crawler 70 crawler_json 3 pre-tracking 2
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Always release locks in finally blocks. Use Redis SET NX EX for distributed locks. Set timeouts on locks to prevent indefinite holds.
📦 Applies To
web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
flock\(|LOCK_EX
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-662


✓ schema.org compliant