Implement a Stoppable Producer–Consumer System in C++
Company: Chicago
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
# Implement a Stoppable Producer–Consumer System in C++
Design and explain a C++ implementation with three classes: `Storage`, `Factory`, and `Consumer`.
### Constraints & Assumptions
- `Storage` is a fixed-capacity FIFO of `int` values.
- `store(item)` is nonblocking: insert and return `true` unless full; if full, do nothing and return `false`.
- `retrieve(result)` is nonblocking: remove the oldest item into `result` and return `true` unless empty.
- One factory and one consumer run concurrently.
- A positive `itemsPerSec` controls each worker's attempt rate.
- The caller, not a hardcoded timer inside a worker, owns shutdown.
### Clarifying Questions to Ask
- Is the required baseline mutex-based, or must a lock-free extension also be implemented?
- How precise must rate scheduling be, and is occasional drift acceptable?
- How quickly must shutdown respond while a worker is waiting for its next period?
### Part 1: Thread-Safe Bounded Storage
Implement the semantics of `store` and `retrieve`. Explain the critical sections and why checking `empty` or `full` separately from mutation is unsafe.
#### What This Part Should Cover
- One lock scope around each check-and-modify operation.
- Correct full and empty behavior with no blocking.
- Exception-safe locking and FIFO semantics.
### Part 2: Rate-Controlled Workers and Shutdown
The factory attempts values `0, 1, 2, ...`; a value rejected because storage is full is dropped and the next attempt uses the next integer. The consumer attempts one retrieval per period. Show how the test requests both workers to stop and joins their threads.
#### What This Part Should Cover
- Rate calculation and handling of a positive rate.
- Item numbering that advances even on a failed store.
- An externally controlled atomic stop flag and bounded stop latency.
### Part 3: Memory Ordering and a Lock-Free Extension
Explain why a plain `bool` stop flag is invalid, when relaxed ordering is sufficient, when acquire/release would be needed, and how a fixed-size single-producer/single-consumer ring buffer could replace the mutex queue.
#### What This Part Should Cover
- Data-race and happens-before reasoning.
- Publication of a written slot before the consumer observes a new tail.
- Safe reuse of a consumed slot after the producer observes a new head.
- Why this SPSC design does not automatically generalize to multiple producers or consumers.
### Solving Hints
- Keep lifetime control separate from the worker loop.
- A condition variable can make a timed wait interruptible even though storage operations remain nonblocking.
- Identify exactly which thread writes each ring-buffer index.
### What a Strong Answer Covers
- Correct baseline code structure, not only concurrency vocabulary.
- Atomicity of compound queue operations and well-defined shutdown.
- Clear trade-offs among mutex simplicity, contention, stop responsiveness, and lock-free complexity.
- Correct memory-order reasoning without claiming that atomics alone make arbitrary structures safe.
### Follow-up Questions
1. How would you prevent long-term rate drift compared with repeated `sleep_for` calls?
2. How can shutdown interrupt a worker sleeping for a long interval?
3. What changes for multiple producers and multiple consumers?
4. Which cache-line or false-sharing concern can affect the SPSC ring buffer?
Quick Answer: Design a stoppable C++ producer-consumer system with a bounded FIFO, nonblocking store and retrieve calls, and rate-controlled workers. Explain mutex critical sections, atomic shutdown, bounded stop latency, memory ordering, and how a single-producer single-consumer ring buffer could provide a lock-free extension.