Process vs thread
- Process: isolated memory space; safer, heavier; IPC to communicate.
- Thread: shares the process's memory; cheap to create/switch, but shared mutable state is where bugs live.
Concurrency (making progress on many tasks by interleaving) isn't the same as parallelism (literally running at once on multiple cores) — an async server is highly concurrent on a few threads.
Race conditions
A race is when correctness depends on the timing of interleaved operations on shared state. The canonical example is the check-then-act / read-modify-write — exactly the StockStump bug:
T1 read balance = 1000
T2 read balance = 1000 ← both saw 1000
T1 write 1000 - 700 = 300
T2 write 1000 - 700 = 300 ← ₹700 spent twice; one write lost
Fix: make the operation atomic — a DB conditional update
(SET balance = balance - cost WHERE balance >= cost), a lock around the
critical section, or an atomic CAS instruction.
Locks & deadlock
A mutex serializes access to a critical section. But locks introduce deadlock — four conditions must all hold (Coffman):
- Mutual exclusion — a resource is held exclusively.
- Hold and wait — hold one, wait for another.
- No preemption — can't force a release.
- Circular wait — a cycle of waiting.
Break any one to prevent deadlock — most practically, impose a global lock ordering (always acquire A before B) to kill circular wait, or use timeouts.
Pessimistic (SELECT … FOR UPDATE, mutex): lock first, assume conflict —
best under high contention. Optimistic (a version column; on write, check
the version is unchanged, else retry): assume no conflict, validate at commit —
best under low contention, no lock held. StockStump's balance is low-contention
per user → optimistic (or a single atomic update) fits.