Critical Section in Process Synchronization: Race Conditions and Correctness Worked Step by Step

See how two sensible updates to x can finish at 6, 7, or 8. The worked trace connects race conditions to mutual exclusion, progress, bounded waiting, and safe synchronization choices.

KnowledgeGate Team

Exam prep & CS education

Updated 21 Oct 20266 min read

Two processes can each execute a perfectly sensible statement and still corrupt a shared result. The trouble begins when the machine interleaves the statement's read, compute, and write steps. A single interleaving trace connects critical section, race condition, mutual exclusion, progress, and bounded waiting, starting with x = 5. Correctness depends on ordering, not memorised definitions in isolation.

Locate the critical section before trying to protect it

A process alternates among an entry section, critical section, exit section, and remainder section. Entry requests permission, exit releases it, and remainder work does not need the protected resource.

The critical section is code that reads or changes shared state, such as a variable, queue, file structure, or device state. The shared object is not the critical section, and neither is the entire process.

Suppose shared integer x = 5. Process P1 executes x = x + 1, while P2 executes x = x + 2. Both statements are critical sections because each performs a read-modify-write operation on the same x. A calculation involving only a process-local value stays in the remainder section. The CS Fundamentals route places this idea within the broader subject.

The three requirements of a correct critical-section solution

Mutual exclusion means that at most one process may be inside its critical section for the protected resource at a time. In this example, P1 and P2 must not overlap the complete read-modify-write sequence on x.

Progress applies when the critical section is empty and processes want to enter. Processes staying in their remainder sections cannot postpone the choice indefinitely.

Bounded waiting is separate. After a process requests entry, there must be a finite bound on how many times others may enter first. Progress prevents needless postponement. Bounded waiting prevents one requester from starving while others enter.

A correctness argument cannot depend on one process always being faster than another. Mutual exclusion alone is also insufficient. An entry protocol may prevent overlap yet deadlock, or it may let a particular requester wait without a finite bound.

Worked trace: two safe-looking assignments lose an update

Treat each load, add, and store as a separate step, so the source assignment is not indivisible. With x = 5, P1 uses LOAD r1 <- x, ADD r1 <- r1 + 1, STORE x <- r1. P2 uses LOAD r2 <- x, ADD r2 <- r2 + 2, STORE x <- r2.

step

P1

P2

shared x

initial

x = 5

1

LOAD r1 = 5

x = 5

2

LOAD r2 = 5

x = 5

3

ADD r1 = 6

x = 5

4

ADD r2 = 7

x = 5

5

STORE x = 6

x = 6

6

STORE x = 7

x = 7

The final value is 7, so P1's increment is lost. In contrast, serial orders give (5 + 1) + 2 = 8 and (5 + 2) + 1 = 8.

If both processes load 5 but the stores are reversed, P1 stores last and the result is 6. If either update finishes before the other load, the second process reads the updated value and produces 8. Thus the exact possible set is {6, 7, 8}. A race condition makes the result depend on timing, and neither 6 nor 7 matches serial execution.

Two-lane timeline of the x = 5 race: both processes load 5, and the interleaved stores leave x = 7 instead of the serial 8.

Why obvious entry protocols fail

Consider a naive shared Boolean lock = 0. P1 reads 0; before it writes, P2 also reads 0. Both write lock = 1 and enter. Because check and set are separate, mutual exclusion fails. With an atomic read-modify-write primitive, only one process can observe and claim the available state.

Strict alternation fails differently. Let turn = 0, use the entry rule while (turn != i), and set turn = 1 - i on exit. If P0 stays in its remainder section while only P1 wants to enter, P1 must wait even though the critical section is empty. Mutual exclusion may still hold, but progress fails.

Disabling interrupts is a kernel technique for limited contexts, not a general user-process proof or a portable multiprocessor solution by itself. Entry and exit need a suitable atomic primitive or proven algorithm, not an ordinary check followed later by an ordinary write.

Three panels: a mutual exclusion failure with lock = 0, a progress failure with turn = 0, and a checklist of the three rules.

Map the problem to the right synchronization tool

A mutex provides lock and unlock around a critical section, normally with ownership semantics. A binary semaphore can gate one unit of access. A counting semaphore initialised to k permits k acquisitions, so it does not give single-resource mutual exclusion when k > 1. A spinlock repeatedly checks atomic state and suits only an understood waiting tradeoff.

Peterson's algorithm is a conceptual bridge for two processes. Intent flags stop an uninterested process from controlling the choice, while turn resolves simultaneous interest. Its textbook proof relies on atomic shared reads and writes plus the model's memory-order assumptions. It is not a production mutex replacement.

After you can enumerate the x = 5 outcomes and identify the first failing entry step, use Process Synchronization and Semaphores to trace Peterson's solution and wait/signal semantics. Then Semaphore Trace Tables for Three Classic Problems applies those semantics to producer-consumer, readers-writers, and dining philosophers.

Traps that make a correct-looking answer wrong

Keep the vocabulary exact. Shared data is the protected resource. Code touching that resource is the critical section. A race condition is timing-dependent behaviour. A race can exist even when one run happens to finish at 8; that single execution does not prove the program safe.

Keep failure properties separate. A mutual-exclusion violation means overlapping entry. Deadlock means participants cannot progress. Starvation means one requester can be bypassed indefinitely while others continue. Busy waiting describes how a process waits, not whether its algorithm is correct.

Check the arithmetic against the trace. Ending at 7 loses P1's +1; reversing only the stores ends at 6 and loses P2's +2; either serial order ends at 8. Under this model, exclude 5 and 9. Never treat source-level x = x + c as atomic unless guaranteed.

How exam-style questions test the critical-section problem

Common question forms ask you to identify the critical section, enumerate final values from an interleaving, match a claim to mutual exclusion, progress, or bounded waiting, find the first violating step in an entry protocol, or decide whether an atomic primitive closes a check-then-set race.

Use these answer-first checks:

  1. Both processes read lock = 0 before either writes, so mutual exclusion can fail.

  2. With turn = 0, uninterested P0, and waiting P1, progress fails.

  3. If P1 can be bypassed 1, 2, 3, ... times with no finite bound, bounded waiting fails.

  4. Protecting the complete LOAD-ADD-STORE update makes either serial order finish at x = 8.

For focused practice, work through Process Synchronization MCQs: 12 Solved GATE Questions and explain each answer using the property or interleaving that decides it.

The short version and the next step

  • Locate the shared read-modify-write code.

  • Show how its individual steps can interleave.

  • Demand mutual exclusion, progress, and bounded waiting.

  • Choose a primitive or algorithm whose atomicity assumptions support those properties.

The unprotected outcomes are {6, 7, 8}, while either protected serial order gives 8. Cover the timeline and reconstruct all six steps from x = 5, then explain aloud why 7 is a race result, not merely a calculation mistake. For wider CS revision, continue with Computer Science Fundamentals for Placements.