C++ Concurrency, Memory Ownership, And RAII
Asked of: Software Engineer
Last updated

What's being tested
Interviewers probe whether you can reason about safe object lifetime, resource cleanup, and concurrent access in production C++ code. They expect you to demonstrate practical use of RAII for deterministic cleanup, correct ownership transfer using move semantics, and the right synchronization primitive choices (`std::mutex` vs `std::atomic`) to avoid data races and undefined behavior. For eBay-scale services, this maps to delivering robust, maintainable components (connection pools, caches, task queues) that don't leak, deadlock, or silently corrupt state under concurrency.
Core knowledge
-
RAII: Resource Acquisition Is Initialization guarantees deterministic cleanup via constructors/destructors; use it for file handles, locks, sockets, and transactions to avoid leaks and ensure exception-safety.
-
Ownership models: Prefer single ownership with
`std::unique_ptr`for exclusive resources; use`std::shared_ptr`for shared ownership and`std::weak_ptr`to break cycles and allow non-owning observers. -
Move semantics: Implement/consume move constructors and
operator=to transfer ownership cheaply; moving invalidates the source but avoids deep copies and double-free errors. -
Lifetime vs pointer validity: Raw pointers are non-owning; never extend lifetime beyond the owning smart-pointer scope. Dangling pointer bugs often occur across thread boundaries without explicit synchronization.
-
Data race definition: Concurrent unsynchronized accesses (one is a write) to the same memory location cause a data race and undefined behavior; use
`std::atomic`or locks to avoid it. -
Mutex primitives: Use
`std::mutex`with`std::lock_guard`for simple scope-based locking and`std::unique_lock`when you need deferred/unlocked locking or`std::condition_variable`waits. -
Atomics and memory ordering:
`std::atomic<T>`provides lock-free primitives where appropriate; understandmemory_order_relaxed,memory_order_acquire,memory_order_release, and acquire-release pairs to reason about visibility (happens-before). -
Compare-and-swap: Use
`compare_exchange_strong`/`weak`carefully for lock-free updates; watch for the ABA problem when reusing memory without versioning. -
Deadlock avoidance: Enforce a global lock order, prefer finer-grained locks, and consider
`std::scoped_lock`for multiple locks; detect possible lock inversions during design. -
Double-checked locking caveats: Correct only with proper synchronization and memory barriers; on weakly ordered architectures, naive double-checked locking is unsafe without
`std::atomic`with acquire/release semantics. -
Reference counting costs:
`std::shared_ptr`has atomic reference updates by default; expect contention and cost in hot paths — measure before choosing shared ownership for high-throughput code. -
Testing and tooling: Use thread sanitizers (
`-fsanitize=thread`), address sanitizers, and static analyzers to catch UB, data races, and lifetime issues early.
Worked example — "Implement a thread-safe cache using RAII and smart pointers"
First 30s: clarify expected semantics — is the cache bounded? Are values immutable once inserted? What concurrency guarantees (concurrent reads, single-writer) are required? Assume a bounded cache with concurrent readers and occasional writers; values are immutable after construction. Skeleton: (1) store entries as `std::shared_ptr<Value>` in a `std::unordered_map<Key, std::shared_ptr<Value>>`, (2) protect the map with a `std::mutex` and `std::lock_guard` for map mutations, (3) return `std::shared_ptr` to callers so lifetime extends outside the lock (RAII ensures cleanup). Tradeoff: this is simple and safe but `std::shared_ptr` incurs atomic refcount overhead on hot reads; alternative is read-write lock or lock-striping to improve read throughput. Flag explicit decisions: if readers vastly outnumber writers, consider `std::shared_mutex` for shared locking or a concurrent hashmap implementation. Close: if more time, I'd add eviction policy (LRU) with consistent ordering under concurrency and benchmark the `shared_ptr` vs copy semantics for hot values.
A second angle — "Design a producer-consumer queue with minimal latency"
Same building blocks (RAII for locks, correct ownership transfer), but constraints shift: low latency and high throughput favor lock-free or wait-free structures. Use a ring buffer with `std::atomic<size_t>` head/tail indices and `std::memory_order_*` for visibility; store elements in slots as `std::unique_ptr<T>` to avoid copies and ensure deterministic destruction. Clarify whether multiple producers/consumers are allowed — single-producer/single-consumer permits much simpler and faster algorithms. Call out tradeoffs: lock-free designs reduce blocking but increase complexity (ABA, false sharing), so use them only when profiling shows lock contention is the bottleneck.
Common pitfalls
Pitfall: Pretending
`std::shared_ptr`makes your code automatically thread-safe.`std::shared_ptr`protects its reference count atomically, but the pointed-to object still requires synchronization for concurrent mutation; copying a`shared_ptr`is safe, modifying the pointee is not.
Pitfall: Returning a raw pointer to an object owned by a local smart pointer. A common mistake is returning
`raw_ptr.get()`from a function while the`shared_ptr`dies at scope exit, producing a dangling pointer and UB. Return a smart pointer or ensure caller owns lifetime.
Pitfall: Mixing locks and atomics without a clear happens-before model. Using
`std::atomic`for some flags and`std::mutex`for related data can leave a gap where readers observe stale or inconsistent state unless acquire-release ordering or explicit locking is used to establish happens-before.
Connections
Interviewers may pivot to lock-free algorithms and hazard pointers, or to the C++ memory model details (sequenced-before, happens-before). They may also ask about testing and observability: using sanitizers, profiling refcount hot spots, or designing metrics for concurrency contention (`lock hold time`, `wait count`).
Further reading
-
C++ Concurrency in Action (Anthony Williams) — practical guide and patterns for modern C++ concurrency.
-
cppreference - C++ Atomics — concise reference for
`std::atomic`and memory orders. -
Herb Sutter — Writing Good C++ Concurrency — collection of posts on pitfalls and best practices for writing concurrent C++.
Related concepts
- C++ Concurrency And Memory Model
- Concurrency And SynchronizationSoftware Engineering Fundamentals
- Concurrency Control And Thread SafetySystem Design
- Concurrency And Thread SafetyCoding & Algorithms
- Concurrency, Deadlocks, And SynchronizationSoftware Engineering Fundamentals
- Concurrency, OS Threads, And MemorySoftware Engineering Fundamentals