Synchronization and concurrency are critical for managing processes that execute simultaneously in a multitasking environment. They ensure data consistency, prevent race conditions, and coordinate access to shared resources through mechanisms like semaphores, mutexes, monitors, and condition variables. Effective concurrency control improves performance while maintaining correctness, making it a key focus area for advanced OS interviews.
1. Explain the difference between deadlock, livelock, and starvation with real-world examples.
Deadlock:
- Two or more processes are stuck indefinitely, each waiting for a resource held by the other.
- Example: Two trains face each other on a single track and both refuse to reverse.
Livelock:
- Processes keep changing their state in response to each other but still make no progress.
- Example: Two people trying to pass in a narrow hallway both step aside repeatedly in the same direction, never crossing.
Starvation:
- A process waits indefinitely because higher-priority processes always get resources first.
- Example: At a buffet, the waiter keeps serving VIP guests while one customer keeps waiting.
Key Difference: Deadlock is a complete halt, livelock is active but unproductive, starvation is indefinite waiting due to unfair scheduling.
2. Why does priority inversion occur, and how can protocols like Priority Inheritance prevent it?
Priority Inversion:
- Happens when a high-priority process is blocked by a low-priority process holding a needed resource.
- The problem worsens if a medium-priority process preempts the low-priority one, delaying the resource release and indirectly stalling the high-priority process.
Solution - Priority Inheritance Protocol (PIP):
- Temporarily boosts the low-priority process’s priority to match the blocked high-priority process.
- This ensures the low-priority process finishes quickly, releases the resource, and restores normal priorities.
Example: NASA’s Mars Pathfinder (1997) encountered system resets due to priority inversion in its VxWorks OS scheduler; enabling priority inheritance resolved the issue.
3. How does the Bakery Algorithm ensure fairness in process synchronization along with the drawbacks?
Each process picks a ticket number before entering the critical section (like standing in line at a bakery). The lowest ticket number gets entry first. If numbers tie, the process ID is used to break the tie.
Fairness:
- Ensures FIFO ordering -> no process can be overtaken indefinitely.
- Prevents starvation, as every process gets its turn eventually.
Drawbacks:
- Needs atomic operations for ticket assignment and comparison to avoid inconsistencies.
- More theoretical than practical in real OSes, but important in algorithm design.
4. Explain how a monitor differs from a semaphore in synchronization.
Semaphore:
- A low-level primitive (wait/signal).
- Programmer is responsible for correct use -> easy to make mistakes (e.g., forgetting signal() -> deadlock).
- Doesn’t enforce structure around shared resources.
Monitor:
- A high-level abstraction combining shared data + synchronization code.
- At most one thread inside at a time (mutual exclusion is built-in).
- Condition variables inside monitors allow threads to wait/signal safely.
- Reduces errors and improves maintainability.
Example: Java’s synchronized methods/blocks implement monitor semantics.
5. What is a Spurious Wakeup in condition variables, and how should it be handled?
Spurious Wakeup:
- A thread waiting on a condition variable may wake up without any signal/broadcast.
- Causes: hardware issues, system-level optimizations, or race conditions.
- This means a waiting thread cannot assume the condition is true after waking up.
Handling:
- Always use a while loop instead of an if when checking the condition:
pthread_mutex_lock(&lock);
while (buffer_empty) {
pthread_cond_wait(¬_empty, &lock); // may wake up spuriously
}
consume_item();
pthread_mutex_unlock(&lock);
- The loop ensures the condition is rechecked before proceeding.
Key Point for Interview:
- Spurious wakeups are rare but important - if ignored, they cause subtle race conditions and incorrect program behavior.
- Correct usage pattern: while (condition == false) wait(), not
if.
6. In the Reader-Writer problem, why is writer starvation common, and how can it be prevented?
Issue - Writer Starvation:
- In reader-preference solutions, as long as new readers keep arriving, they are granted access first.
- This means writers may be postponed indefinitely, even if they have been waiting longer -> starvation.
Prevention Methods:
- Writer-Priority Policy -> Once a writer is waiting, no new readers are allowed to start until all writers finish.
- Fair Scheduling (FIFO Queues) -> Threads (readers/writers) are served in arrival order, ensuring bounded waiting.
- Timeout Mechanisms -> Writers waiting too long get elevated priority.
Real-world Example: In databases, write locks are prioritized over read locks to ensure updates aren’t blocked indefinitely, preventing stale data issues.
7. How do condition variables in monitors help solve producer-consumer problems?
Problem:
- Producer must wait if the buffer is full, and consumer must wait if the buffer is empty.
- Without condition variables -> processes must busy-wait (poll continuously), wasting CPU cycles.
Condition Variables in Monitors:
- Provide wait() (thread suspends until signaled) and signal() (wakes one waiting thread).
- Used with a mutex lock to safely check conditions.
Application in Producer–Consumer:
- Producer: If buffer is full -> wait(not_full). After producing -> signal(not_empty).
- Consumer: If buffer is empty -> wait(not_empty). After consuming -> signal(not_full).
Benefit:
- Eliminates busy waiting.
- Improves efficiency, responsiveness, and fairness in concurrent systems.
8. Describe a situation where using a binary semaphore would cause deadlock but a monitor would not.
Binary Semaphore Deadlock Case: Consider two processes P1 and P2 trying to access two shared resources R1 and R2 protected by binary semaphores S1 and S2:
- P1 acquires S1 (locks R1), then tries to acquire S2.
- P2 acquires S2 (locks R2), then tries to acquire S1.
- Both are now waiting for each other -> circular wait -> deadlock.
Reason: With semaphores, the programmer must explicitly manage acquire/release. If resources are not acquired in a consistent order, deadlock is possible.
Why Monitors Avoid This: A monitor wraps the shared resources and their synchronization inside a structured abstraction:
- Only one process/thread can be inside the monitor at a time (implicit mutual exclusion).
- Threads don’t manually acquire multiple binary semaphores in arbitrary order -> no circular wait on separate locks inside a single monitor.
Thus, in the same scenario, only one thread at a time would access the critical section guarded by the monitor, avoiding the deadlock situation.
9. How can spinlocks be more efficient than mutexes, and when are they harmful?
Why Spinlocks Can Be More Efficient:
- Avoid kernel involvement -> no context switching overhead.
- Great when lock hold-time is very short (nanoseconds–microseconds).
- Especially efficient on multi-core CPUs, where the waiting thread can spin on a different CPU.
When They’re Harmful:
- Long waits -> CPU cycles wasted in busy loops.
- Can starve other processes (since CPU is occupied spinning).
- On single-core systems, they’re dangerous (CPU can’t make progress if one thread keeps spinning).
Rule of Thumb: Use spinlocks only for short, predictable waits (e.g., kernel interrupt handlers); otherwise, prefer mutexes.
10. Explain the role of the “Happens-Before” relationship in concurrency control.
A logical relationship used to reason about the order of visibility of operations in concurrent programs.
- If A happens-before B, then effects of A are visible to B.
- If no happens-before relationship exists -> operations are considered concurrent -> may cause race conditions.
Uses:
- Guarantees memory visibility and ordering.
- Prevents stale or inconsistent reads.
Example (Java Memory Model):
- A synchronized block unlock happens-before any subsequent lock on the same monitor.
- A write to a volatile variable happens-before subsequent reads of that variable.
Key Point: Happens-before defines the rules of causality in concurrency, ensuring correctness across threads.
11. How does priority inversion occur in concurrent systems, and what are its real-world consequences? Explain solutions.
Priority inversion Occurs when a high-priority process is waiting on a lock/resource held by a low-priority process, but a medium-priority process keeps preempting the low-priority one. This prevents progress of the high-priority task
Example: NASA’s Mars Pathfinder mission suffered resets due to unhandled priority inversion in its task scheduler.
Solutions:
- Priority Inheritance Protocol (PIP): Temporarily raise the priority of the low-priority task holding the lock to match the high-priority task.
- Priority Ceiling Protocol (PCP): Each resource has a predefined ceiling priority; processes borrowing it temporarily assume that priority, preventing medium-priority preemption.
Note: Without mitigation, system responsiveness, predictability, and correctness can degrade severely in real-time systems. Priority inversion is not just theoretical - real-time and embedded systems must handle it explicitly.
12. Differentiate between Busy Waiting and Blocking in synchronization. Which is better in high-concurrency systems?
Busy Waiting (Spinlocks):
- Process loops actively checking if a lock is free.
- Wastes CPU cycles but avoids costly context switching.
- Best when lock-hold times are very short.
Blocking:
- Process is suspended until resource becomes available.
- Saves CPU cycles but involves scheduler overhead.
- Best when lock-hold times are long/unpredictable
Trade-off:
- On multi-core CPUs: short waits -> spinlocks are more efficient.
- On single-core or unpredictable workloads: blocking ensures better throughput and fairness.
Rule of Thumb: Spinlocks for nanosecond–microsecond waits, blocking for anything longer.
13. How do condition variables differ from semaphores in process synchronization? Give an example where semaphores fail but condition variables work.
Semaphores:
- Counting mechanism.
- Can be signaled even if no thread is waiting (signal persists).
- Suitable for resource counting.
Condition Variables:
- Always used with a mutex.
- Signals are lost if no thread is waiting (state-dependent).
- Suitable for state-based synchronization (e.g., "buffer empty" -> wait).
Example where semaphores fail but condition variables work: Producer–Consumer problem
- With Semaphore: If producer signals before consumer starts waiting, the consumer won’t miss the event (count increases).
- With Condition Variable: Consumer sleeps until state changes (e.g., buffer not empty). This avoids missed wake-ups when synchronization is about state, not just counts.
Note: Use Semaphores for resource availability (e.g., # of slots/items). Use Condition Variables for state changes (e.g., buffer empty/full, predicate-based waits).
14. Explain the Dining Philosophers problem and how the Chandy/Misra solution improves over classical methods.
Classical Problem:
- Five philosophers sit around a table with forks between them. Each philosopher needs two forks to eat.
- If all philosophers pick up their left fork first, a deadlock occurs (everyone waits forever for the right fork).
- Even with solutions like ordering fork acquisition, starvation may still occur (some philosophers may never get a chance).
Chandy/Misra Solution:
- Forks are modeled as tokens that can be in a clean or dirty state.
- Only a philosopher holding a fork (token) may eat. After eating, they mark the fork as dirty and must pass it on if requested.
- Forks are requested and passed asynchronously, eliminating the need for all-or-nothing blocking.
Benefits:
- Prevents circular wait -> no deadlock.
- Improves fairness since dirty forks are more likely to be passed.
- Works well in distributed systems without a central controller.
Note: Chandy/Misra’s algorithm shifts from centralized locking to token-based distributed synchronization, ensuring fairness and avoiding deadlock.
15. What is the difference between Read–Write locks and Binary locks? When would Read–Write locks cause performance degradation?
Binary Locks (Mutexes):
- Only one thread at a time (whether reader or writer).
- Simple but inefficient in read-heavy workloads.
Read–Write Locks (RW Locks):
- Allow multiple readers concurrently as long as no writer is active.
- Writers require exclusive access.
- Much better in read-dominant workloads (e.g., caching, database queries).
Performance Degradation in RW Locks:
- In workloads with many readers and frequent writers, readers may continuously acquire the lock, leading to writer starvation.
- If writers are prioritized, then readers may starve instead.
- Mitigation: Fair scheduling policies (FIFO queues or writer-preference variants).
Note: RW locks outperform binary locks in read-heavy workloads, but mismanagement can lead to writer starvation.
16. How does OS handle synchronization in SMP (Symmetric Multiprocessing) systems differently from uniprocessor systems?
Uniprocessor: Only one CPU executes at a time, so synchronization can often be ensured by disabling interrupts to prevent context switches during critical sections.
SMP (Symmetric Multiprocessing): Multiple CPUs may execute concurrently and access shared memory. Disabling interrupts is insufficient since other CPUs can still run.
- OS uses spinlocks and scalable synchronization primitives.
- Cache coherence protocols (MESI, MOESI) prevent CPUs from seeing stale data.
- In performance-critical paths, lock-free algorithms are preferred to minimize contention.
SMP Challenges: Multiple CPUs may modify shared data concurrently. Synchronization primitives must be scalable to avoid bottlenecks.
17. Compare Monitor-based synchronization and Lock-based synchronization in terms of safety and maintainability.
Lock-based: Programmer explicitly acquires/releases locks (mutexes, semaphores).
- Pros: Fine-grained control.
- Cons: Error-prone (risk of deadlocks, forgetting to release locks).
Monitor-based: High-level abstraction where mutual exclusion is implicit. The language or runtime acquires the lock on method entry and releases on exit.
- Pros: Safer, easier to maintain, reduces programming errors.
- Cons: Less flexible for specialized synchronization patterns.
Note: Monitors improve safety and maintainability, while locks offer flexibility but more risk of error.
18. What is a Barrier in concurrency? Explain a scenario where barriers can cause deadlock.
Barrier in Concurrency:
- A barrier is a synchronization primitive where all threads (or processes) must reach a certain point of execution before any of them can proceed further.
- It is commonly used in parallel programming to coordinate phases of computation (e.g., all threads complete Phase 1 -> then all move to Phase 2).
Deadlock Scenario with Barriers:
- Problem: If one thread is delayed indefinitely (e.g., due to crash, infinite loop, or starvation), other threads will wait forever at the barrier.
- Example: In a parallel matrix multiplication, threads compute partial results and then synchronize at a barrier before combining results. If one thread fails before reaching the barrier -> the entire system stalls.
Solution: Use timeout-based barriers (threads proceed or abort if some don’t arrive within a time limit). Adopt fault-tolerant designs where missing participants are skipped or replaced.
19. How do optimistic concurrency control techniques work in OS-level transaction management, and what is their drawback?
Working: OCC assumes conflicts are rare. Transactions execute freely without locks, recording read/write sets. At validation (commit) time, the OS checks whether another transaction modified the same data.
- If no conflict -> commit succeeds.
- If conflict detected -> transaction rolls back and restarts.
Advantages:
- Eliminates blocking and deadlocks since no locks are held.
- Very efficient in low-contention workloads (e.g., read-heavy systems).
Drawback:
- In high-contention environments, frequent rollbacks waste CPU cycles and reduce throughput.
- Rollbacks may also cascade if multiple transactions depend on conflicted data.
Note: OCC lets transactions run without locks and checks for conflicts only at commit. It’s efficient in low-contention scenarios but suffers from high rollback overhead when many processes compete for the same data.
20. How does the OS implement fairness in semaphore-based synchronization to avoid starvation?
Fairness in Semaphore-Based Synchronization
- Problem: In naive semaphore implementations, processes may acquire the resource in arbitrary order, leading to starvation if some processes are repeatedly bypassed.
- Fair Implementation: The OS ensures fairness by maintaining a FIFO wait queue for blocked processes. When a signal() (V operation) occurs, the process at the head of the queue is unblocked first.
- Example: In Linux, semaphores maintain such wait queues, so processes are awakened in arrival order, preventing indefinite delays and ensuring bounded waiting.
The OS avoids starvation in semaphores by using queue-based scheduling (FIFO) instead of arbitrary wakeups, guaranteeing fairness and predictable resource access.