1. Introduction

A Race Condition is one of the most fundamental and dangerous problems in concurrent systems. It occurs when multiple processes or threads access and modify shared data simultaneously without proper synchronization, causing the final result to depend on the unpredictable order of execution.

In concurrent systems, processes may execute at different speeds and may be interrupted at any point by the operating system scheduler. If shared resources are accessed without coordination, different execution orders can produce different results.

Formally:

A race condition occurs when the outcome of a program depends on the relative timing or interleaving of concurrently executing processes or threads.

The term "race" comes from the fact that multiple processes are effectively racing to access and modify shared data.

Race conditions are among the primary reasons synchronization mechanisms such as mutexes, semaphores, and monitors exist.

2. Core Idea

A race condition typically occurs when the following conditions are present:

  • Multiple processes or threads execute concurrently.

  • Shared data or resources exist.

  • Processes modify the shared resource.

  • Proper synchronization is absent.

When all these conditions occur simultaneously:

Concurrent Execution
         +
Shared Resource
         +
No Synchronization
         ↓
Race Condition

The result becomes:

Non-Deterministic

meaning the output cannot be predicted reliably.

The same program may produce different results during different executions.

3. Classic Example

Consider a shared variable:

int x = 0;

Two processes execute concurrently.

Process P1

x = x + 1;

Process P2

x = x + 1;

At first glance:

0 + 1 + 1 = 2

so we expect:

x = 2

However, concurrent execution can produce different results.

4. Expected vs Actual Output

Expected Output

Both increments should be applied.

Initial x = 0

P1 adds 1

P2 adds 1

Final x = 2

Possible Actual Output

x = 1

which is incorrect.

The discrepancy occurs because increment operations are not atomic.

5. Step-by-Step Execution

To understand the race condition, consider how:

x = x + 1;

is actually executed internally.

The CPU performs:

Read x
   ↓
Add 1
   ↓
Write x

This involves multiple machine instructions.

Suppose the following interleaving occurs.

Step 1

P1 reads:

x = 0

Step 2

P2 reads:

x = 0

Step 3

P1 computes:

0 + 1 = 1

and writes:

x = 1

Step 4

P2 computes:

0 + 1 = 1

and writes:

x = 1

Final Value

x = 1

instead of:

x = 2

One update is effectively lost.

This phenomenon is called a:

Lost Update Problem

and is one of the most common manifestations of race conditions.

6. Conceptual Visualization of Race Condition

Consider the timeline below.

Time →

P1: Read x
P2:      Read x

P1:             Write 1
P2:                    Write 1

Both processes read the same old value.

As a result:

One Update Overwrites Another

The final result becomes incorrect.

7. Why Race Conditions Occur

Race conditions arise because of four key factors.

7.1 Concurrent Execution

Multiple processes or threads execute simultaneously.

Example:

P1 Running

P2 Running

at the same time.

Without concurrency:

No Race Condition

can occur.

7.2 Shared Resources

Processes access common data such as:

  • Variables

  • Files

  • Memory

  • Databases

  • Buffers

Example:

int counter;

shared among processes.

7.3 Lack of Synchronization

Processes access shared resources without coordination.

Example:

No Locks

No Semaphores

No Mutexes

This permits simultaneous access.

7.4 Non-Atomic Operations

Many operations that appear simple are actually multiple steps.

Example:

counter++;

internally becomes:

Read Counter

Increment

Write Counter

Interruption between these steps causes race conditions.

8. Key Insight

A very important observation is:

Concurrency itself is not the problem.

Modern operating systems rely heavily on concurrent execution.

The real problem is:

Uncontrolled concurrent access to shared resources.

Thus:

Concurrency
        +
Synchronization
        ↓
Safe

but:

Concurrency
        +
No Synchronization
        ↓
Race Condition

9. Relationship with Critical Section

Race conditions and critical sections are closely related.

Critical Section

A critical section is:

Code That Accesses
Shared Resources

Race Condition

A race condition occurs when:

Multiple Processes
Enter Critical Section
Simultaneously

and modify shared data.

Thus:

Critical Section Problem
          ↓
Exists To Prevent
          ↓
Race Conditions

The primary purpose of synchronization is to ensure that only one process executes the critical section at a time.

10. Real-World Analogy

Consider a shared bank account.

Initial balance:

₹100

Two people simultaneously deposit:

₹50

each.

Person A

Reads:

Balance = ₹100

Computes:

₹150

Person B

Also reads:

Balance = ₹100

Computes:

₹150

Both write:

₹150

Final balance becomes:

₹150

instead of:

₹200

This is a race condition.

11. Effects of Race Conditions

Race conditions can have severe consequences.

11.1 Incorrect Results

Programs produce wrong outputs.

Example:

Expected = 2

Actual = 1

11.2 Data Corruption

Shared data structures become inconsistent.

Examples:

  • Corrupted files

  • Invalid database records

  • Damaged memory structures

11.3 System Crashes

Critical race conditions may cause:

Invalid State
      ↓
Crash

11.4 Security Vulnerabilities

Many cybersecurity vulnerabilities arise due to race conditions.

Examples include:

  • Privilege escalation

  • Unauthorized access

  • File system attacks

11.5 Unpredictable Behavior

The same program may produce:

Correct Result

today and:

Incorrect Result

tomorrow.

This makes debugging extremely difficult.

12. How to Prevent Race Conditions

The fundamental solution is:

Ensure controlled access to shared resources.

Only one process should modify shared data at a time.

Several synchronization mechanisms achieve this.

12.1 Mutex Locks

A process must acquire a lock before entering the critical section.

Lock
 ↓

Critical Section

 ↓
Unlock

12.2 Semaphores

Semaphores maintain counters that regulate access.

They support:

  • Mutual exclusion

  • Resource management

12.3 Monitors

High-level synchronization constructs that automatically enforce mutual exclusion.

12.4 Atomic Operations

Certain hardware instructions execute indivisibly.

Examples:

Test-And-Set

Compare-And-Swap

These eliminate race conditions for specific operations.

13. Conceptual Solution

The standard synchronization model is:

Acquire Lock
      ↓
Access Shared Resource
      ↓
Release Lock

Example:

lock.acquire();

x = x + 1;

lock.release();

Now the execution becomes:

P1 Executes Completely
      ↓
P2 Executes Completely

No interleaving occurs.

The race condition is eliminated.