C++ Module 16
Concurrency: threads, atomics and the memory model interview questions
Interview questions on “Concurrency: threads, atomics and the memory model”: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What happens if a std::thread object is destroyed while it is still joinable, and how do you avoid it?
Answered in the lesson Threads with std::thread and std::jthread
-
Fresher to mid
What is the difference between a data race and a race condition?
Answered in the lesson Data races, mutexes and lock guards in C++
-
Fresher to mid
Why should a condition-variable wait always use a predicate?
Answered in the lesson C++ condition variables and producer-consumer queues
-
Fresher to mid
What is the difference between std::launch::async and std::launch::deferred, and what does std::async do without a policy?
-
Fresher to mid
What does std::atomic<int> guarantee that a plain int does not, and what does it not guarantee?
Answered in the lesson C++ atomics, compare-exchange and atomic waiting
-
Fresher to mid
What does memory_order::relaxed guarantee for an atomic operation, and what does it not guarantee?
Answered in the lesson The C++ memory model and memory orders
-
Fresher to mid
What is the difference between std::latch and std::barrier, and when would you use each?
Answered in the lesson Latches, barriers and semaphores (C++20)
-
Fresher to mid
Why use a thread pool instead of starting a std::thread for every task?
-
Fresher to mid
What does ThreadSanitizer find, and which concurrency bugs can it miss?
Answered in the lesson Concurrency-bug hunt: races and deadlocks
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
A service starts a std::thread for every request to write an audit record, and detaches it. Under load its memory grows, some records are missing after a restart, and the process sometimes crashes while shutting down. Explain each symptom and redesign the code.
- What it tests
- Lifetimes of detached threads, what happens to them when main returns, unbounded thread creation, and a design for background work that is bounded, owned and shut down on purpose.
- First step
- Ask who owns each thread and when it ends. A detached thread has no owner: nothing limits how many exist, nothing waits for them when the process exits, and nothing keeps alive the objects they use. Match each symptom to one of those three facts before proposing a fix.
- Follow-ups
-
- How many threads can a process start, and what happens past the limit?
- How do you make shutdown wait for pending records without hanging forever?
- Would std::async solve it?
From the lesson Threads with std::thread and std::jthread
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A program has worker threads that wait for jobs in a queue, sleep between polls or block reading a socket. It must exit within one second of Ctrl-C, without losing the job each worker is doing. How do you design the shutdown?
- What it tests
- Cooperative cancellation with std::stop_token, waits that wake on a stop, what a signal handler may do, and budgeting the time a shutdown takes.
- First step
- List every place where a worker can block or run for a long time without checking for a stop: the queue wait, the sleep, the socket read and any long computation. Each one needs a way to notice a stop request within the budget, so the design starts from that list, not from the signal handler.
- Follow-ups
-
- Why not call request_stop() directly in the signal handler?
- What if one worker ignores the token?
- How do you test shutdown?
From the lesson Threads with std::thread and std::jthread
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A teammate argues that the data race on a plain bool shutdown flag is harmless, because storing a bool is a single instruction on every processor the product runs on. How do you answer, and what do you change?
- What it tests
- Why a data race is undefined behaviour whatever the hardware, what compilers may do with a loop that reads an unsynchronised flag, and choosing a correct replacement.
- First step
- Separate the hardware question from the language question. The store may well be indivisible, but the program is written in C++, where any data race makes the behaviour undefined. Ask what the compiler is then allowed to assume about a loop that reads the flag and contains no synchronisation.
- Follow-ups
-
- Does volatile bool fix it?
- Which memory order should the flag use?
- How would you catch this in CI?
From the lesson Data races, mutexes and lock guards in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A payment service moves money between wallets by locking the source wallet's mutex and then the destination's. Under heavy load it occasionally freezes, with the processor almost idle. Explain the most likely cause, how you would confirm it, and two fixes with their trade-offs.
- What it tests
- Recognising a lock-order deadlock from its symptoms, confirming it from thread stacks, and choosing between std::scoped_lock, a global lock order and coarser locking.
- First step
- Read the symptom: a freeze with the processor idle means threads are waiting, not working. Ask what they could be waiting for in this code: two transfers between the same pair of wallets, in opposite directions, at the same moment. Then plan how to see that in the stacks of the frozen threads.
- Follow-ups
-
- What if a transfer can touch three wallets, with a fee wallet too?
- Why can a test suite pass for months with this bug?
- Would std::recursive_mutex help?
From the lesson Data races, mutexes and lock guards in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Senior to staff
An EventBus guards its subscriber list with a std::mutex and runs every callback while holding it. Some callbacks publish events or unsubscribe. The program hung under load; replacing the mutex with std::recursive_mutex stopped the hangs, but now it sometimes crashes inside the loop over subscribers. Explain both failures and redesign it.
- What it tests
- Re-entrancy through callbacks, locking one std::mutex twice from one thread, iterator invalidation, and designs that call out without holding a lock.
- First step
- Follow one callback that publishes an event: publish() holds the mutex, calls the callback, and the callback calls publish() again on the same thread, which locks the same std::mutex a second time. Then follow a callback that unsubscribes while the outer publish() is still looping over the list.
- Follow-ups
-
- Is std::recursive_mutex a good fix?
- How do you guarantee that a callback is never called after unsubscribe() returns?
- What does copying the subscriber list cost, and how do you reduce it?
From the lesson Data races, mutexes and lock guards in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
In a production queue, consumers wait with a 500 ms timeout. Logs show consumers sometimes sleeping for the full 500 ms while an item is already in the queue. List the likely causes and explain how you would prove which one it is.
- What it tests
- Lost wake-ups and their causes: state changed outside the mutex, notifying before the change, the wrong condition variable or waiter, missing notifications, and how to make a rare window visible.
- First step
- Start from what a condition variable is: it remembers nothing, so a consumer misses an item only if no notification reached it while it was waiting. List every place that changes the queue's state and check two things at each: is the change made under the mutex, and which condition variable is notified afterwards?
- Follow-ups
-
- Why does the item get consumed eventually anyway?
- Would notify_all() everywhere fix it?
- Is a timeout a good safety net?
From the lesson C++ condition variables and producer-consumer queues
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
Design the queue between request threads that produce log records and one thread that writes them to a file. What happens when the writer falls behind, and how does shutdown work without losing records?
- What it tests
- Bounded queues and backpressure, sizing from rates and stalls, the choice between blocking and dropping, batching, and an orderly drain at shutdown.
- First step
- Put numbers on the problem before choosing a design: how many records a second, how large, and how long the disk can stall. Those numbers decide the capacity, and the product decides what happens when it is full: wait, or drop and count.
- Follow-ups
-
- Why one writer thread and not several?
- How do you keep a slow logging call from adding latency to every request?
- What about records logged during shutdown, after close()?
From the lesson C++ condition variables and producer-consumer queues
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
A bounded queue uses one condition variable for both "not full" and "not empty", and calls notify_one() after every push and pop. Under load, with several producers and consumers, it sometimes stalls with every thread waiting. Why, and what are the fixes?
- What it tests
- How notify_one() chooses a waiter, building the interleaving that stalls, the cost of notify_all(), and when two condition variables with notify_one() are enough.
- First step
- Notice that one condition variable now has two kinds of waiters, producers and consumers, and notify_one() wakes an arbitrary one of them. Try to build a sequence, with capacity 1 and two threads of each kind, in which every notification reaches a waiter that cannot make progress.
- Follow-ups
-
- Is two condition variables with notify_one() now always correct?
- Why not notify while holding the mutex, to be safe?
- Could a semaphore replace the condition variables?
From the lesson C++ condition variables and producer-consumer queues
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A loop calls std::async(std::launch::async, upload, file) for 20 files without keeping the returned futures. It takes as long as uploading the files one by one. Explain why, and fix it.
- What it tests
- The blocking destructor of futures from std::async, [[nodiscard]], keeping and reading futures, argument lifetimes, and bounding the number of tasks that run at once.
- First step
- Ask what happens to the value that std::async returns: it is a temporary std::future, destroyed at the end of the statement. Then recall what the destructor of a future returned by std::async does while its task is still running.
- Follow-ups
-
- Why does it compile without any visible warning in some builds?
- How do you limit the uploads to 8 at a time?
- Is std::launch::deferred a way around the waiting?
From the lesson std::async, futures and promises
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A worker thread fulfils a std::promise with its result. In production the consumer sometimes blocks in get() for ever, and sometimes gets std::future_error with broken_promise. What went wrong in each case, and how do you make sure the consumer always receives a value or an error?
- What it tests
- Promise lifetimes and ownership, what an abandoned promise stores, forwarding exceptions with set_exception, std::packaged_task, and deadlines on the reading side.
- First step
- Separate the two symptoms by what they say about the promise object: a get() that waits for ever means the promise still exists but was never fulfilled, while broken_promise means it was destroyed unfulfilled. Then list every exit path of the worker and check which ones skip set_value().
- Follow-ups
-
- What happens if set_value() is called twice?
- Where does the exception go if nobody ever calls get()?
- Can the reader cancel the work through the future?
From the lesson std::async, futures and promises
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You must run 10,000 independent tasks of about 50 microseconds each and combine their results. Would you start each one with std::async? Defend your answer with numbers.
- What it tests
- What std::async costs per call, thread creation against task size, thread pools and chunked distribution of work, and when std::async is still the right tool.
- First step
- Estimate both sides of the trade: the total work, 10,000 × 50 µs = 0.5 s, and what std::async does for each call with the async policy, and what that costs. Compare the overhead with the work before choosing a design.
- Follow-ups
-
- Would std::launch::deferred avoid the thread cost?
- How do you choose the chunk size if tasks vary in cost?
- What if each task must time out individually?
From the lesson std::async, futures and promises
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Many request threads must record the largest latency seen so far in a shared variable, without a mutex. Write the update, explain why it is correct, and describe what happens under heavy contention.
- What it tests
- Building a read-modify-write from compare-exchange, what a failed exchange returns, weak against strong, lock-freedom, and the cost of retries under contention.
- First step
- Notice that no single atomic operation stores the larger of two values, so the update must be built: read the current maximum, and replace it only if it is still the value you read and yours is larger. Then decide what the code must do when another thread changed the maximum in between.
- Follow-ups
-
- Why compare_exchange_weak and not compare_exchange_strong?
- How would you cut the contention if every request updates it?
- Can the same pattern keep the 99th percentile?
From the lesson C++ atomics, compare-exchange and atomic waiting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A service increments one global std::atomic<long> for every request. At 32 threads the profile shows that line near the top, although an increment is a single instruction. Why is it slow, and what would you change?
- What it tests
- Cache-line ownership and contention, why atomics on one line take turns, false sharing, sharded or per-thread counters with padding, and measuring before and after.
- First step
- Ask what the hardware must do for one increment when 32 cores take turns: each fetch_add needs the cache line in an exclusive state on its own core, so the line travels between cores on almost every increment. The instruction is short; waiting for the line is not.
- Follow-ups
-
- Would memory_order_relaxed fix it?
- How far apart must the shards be?
- How do you read a sharded counter, and is the result exact?
From the lesson C++ atomics, compare-exchange and atomic waiting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A teammate replaced a std::mutex with a spin lock built on std::atomic_flag to make a hot path faster. On a busy 16-core server, processor use went to 100 % and throughput fell. Explain what happened, and what you would use instead.
- What it tests
- How spin locks behave under contention and with more threads than cores, what std::mutex does instead, atomic waiting, and when a spin lock can be justified.
- First step
- Ask what each waiting thread does while the lock is taken: it keeps a core busy testing the flag. Then ask what happens when there are more runnable threads than cores, and the thread that holds the lock is not the one that is running.
- Follow-ups
-
- Does adding wait() and notify_one() make it as good as a mutex?
- When is a spin lock the right tool?
- How would you prove the mutex is better here?
From the lesson C++ atomics, compare-exchange and atomic waiting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A writer fills a struct, then sets a std::atomic<bool> ready with memory_order::relaxed; readers spin on ready, also relaxed, then read the struct. Every test passes on the x86-64 build servers, but customers on Arm servers report half-filled records. Explain the failure and fix it.
- What it tests
- Happens-before and publication, why relaxed atomics publish nothing, what x86-64 and Arm reorder, and why tests that pass on x86-64 prove nothing about this code.
- First step
- Name the broken rule: the struct is ordinary data written by one thread and read by others, so the reads must happen after the writes. A relaxed store and a relaxed load create no synchronizes-with edge, so the program has a data race. Then explain why x86-64 hides it: that hardware keeps stores in order and loads in order.
- Follow-ups
-
- Is a release store enough if the readers keep their relaxed load?
- How would you show the bug without customer traffic?
- What does the fix cost on x86-64 and on Arm?
From the lesson The C++ memory model and memory orders
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Two threads each set their own std::atomic<int> flag with a release store, then read the other's flag with an acquire load, and enter a critical section only if that flag is 0. Under load, both threads are sometimes inside at once. Why, and what are your options?
- What it tests
- Store buffering, what release and acquire order and what they do not, the single total order of seq_cst operations, fences, and knowing when a mutex is the better answer.
- First step
- Write the four operations down, two per thread in program order, and list the outcomes the memory orders allow. Then ask, for each load that returns 0, which store it read and whether anything synchronizes with anything.
- Follow-ups
-
- Why does seq_cst fix it, in the terms of the standard?
- Would a seq_cst fence on each side be enough with relaxed operations?
- Why might this code pass every test on one Arm machine?
- When would you replace the flags with a mutex?
From the lesson The C++ memory model and memory orders
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
In a code review you find a cache created on first use: if cache is null, lock a mutex, check again, and only then set cache = new Cache(load()), where cache is a plain pointer. The author says the second check under the lock makes it safe. What do you say, and what do you suggest instead?
- What it tests
- Recognising double-checked locking and what makes it unsafe, what an atomic pointer fixes and what it does not, and the thread-safe alternatives of the standard with what they cost.
- First step
- List every access to the pointer and to the object's fields, and mark for each pair in different threads what orders them: a lock, an atomic operation, or nothing. Start with the first check, which runs without the lock.
- Follow-ups
-
- Would std::atomic<Cache*> with default loads and stores fix it?
- What does each fix cost on every call after the first?
- What if the cache must be rebuilt every hour?
From the lesson The C++ memory model and memory orders
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Sixty-four worker threads share a database that allows at most 8 open connections from your service. Design the limit with the C++20 library. What happens when a query throws, when the database is slow, and when the service must shut down?
- What it tests
- Choosing a counting semaphore and its bound, keeping permits safe when a query throws, deadlines with try_acquire_for, throughput from query times, and shutdown and fairness when many threads wait.
- First step
- Name the type and its numbers: a std::counting_semaphore<8> that starts at 8, one permit per open connection, so at most 8 threads hold one. Then follow one query that throws between acquire() and release(), and ask what happens to its permit.
- Follow-ups
-
- How do you stop 56 waiting threads from piling up when the database is slow?
- Is the semaphore fair to the threads that have waited longest?
- How do you shut down while threads are waiting for a permit?
From the lesson Latches, barriers and semaphores (C++20)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A parallel simulation uses a std::barrier between time steps. With one thread its output matches the reference; with eight threads the numbers differ slightly from run to run. Where do you look, and how do you prove the fix?
- What it tests
- What a barrier orders, work that must happen once per phase, floating-point reductions in a changing order, reproducing a timing-dependent result, and what ThreadSanitizer can and cannot confirm for barrier code.
- First step
- Look for anything that touches shared state after arrive_and_wait() returns and before the next phase reads it: a buffer swap, a step counter, a reduction finished by one thread. Then look at how partial results are combined, because adding floating-point numbers in a different order changes the last digits without any race.
- Follow-ups
-
- Why does the one-thread run always match?
- How would you make the bug fail every time in a test?
- Can ThreadSanitizer confirm the fix on a Mac?
From the lesson Latches, barriers and semaphores (C++20)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Service startup waits on a std::latch that six initialiser threads count down. About once in a few hundred restarts, startup hangs for ever. What are the likely causes, how do you find the culprit, and how do you make startup fail loudly instead of hanging?
- What it tests
- Failure paths that skip count_down(), the missing timed wait of std::latch, making every path count down, and reporting an initialiser's failure to main as data.
- First step
- List every way an initialiser thread can end, normally or not, and check that each one counts the latch down. Remember that std::latch has no timed wait, so a single missed count_down() keeps startup waiting for ever.
- Follow-ups
-
- How do you find which initialiser did not count down?
- How do you give startup a deadline?
- How should an initialiser report its failure to main?
From the lesson Latches, barriers and semaphores (C++20)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service runs report tasks on a pool of 8 workers. Each report task splits its work into 4 subtasks, submits them to the same pool and waits for their futures. It works in testing but hangs under production load. Explain the hang precisely and give two fixes with their trade-offs.
- What it tests
- Recognising a deadlock between tasks of one pool, what the pool's size changes and what it does not, and the fixes with what each one costs.
- First step
- Count the workers, and ask what each one is doing when the hang starts: which tasks hold a worker while they wait, and which tasks are still in the queue, waiting for a free worker.
- Follow-ups
-
- Why doesn't it happen in testing?
- Would doubling the pool to 16 workers fix it?
- How would you detect it in production before users notice?
From the lesson Building a thread pool in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Design the shutdown of a thread pool in a payment service: tasks are queued, some are running, and new submissions keep arriving. What should happen to each, what do callers holding futures see, and how do you bound how long the shutdown takes?
- What it tests
- Shutdown policies, refusing new work, what the futures of dropped tasks report, cooperative cancellation of running tasks, destruction order and deadlines.
- First step
- Separate the three groups, new submissions, queued tasks and running tasks, and decide a policy for each before any code: refuse new work with an error, drain or drop the queue depending on whether lost work is acceptable, and decide how long running tasks may take to finish.
- Follow-ups
-
- What does a caller whose task was dropped see?
- How do you stop a task that is already running?
- Which work must never be dropped, and how do you keep it?
From the lesson Building a thread pool in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A pool on an 18-core server runs two kinds of task: image resizing that takes about 20 ms of processor time, and calls to a storage service that wait about 50 ms. How many workers do you start, and do you use one pool or two? Justify it with numbers.
- What it tests
- Sizing work that computes and work that waits, why mixing both in one queue hurts latency, and measuring instead of guessing.
- First step
- Size each kind on its own, from what one task does with its time: does it keep a core busy for its 20 ms, or does it sit idle for 50 ms while it waits? Then ask what happens to resizes when both kinds share one queue.
- Follow-ups
-
- What happens to resize latency if both share one pool of 18?
- How would you check the numbers on the real server?
- What changes if the storage calls become asynchronous?
From the lesson Building a thread pool in C++
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service hangs at shutdown about once in every few thousand deploys. Its job queue's workers wait on a condition variable, and close() sets an atomic flag and calls notify_all(). How do you find the bug, prove the fix, and keep it from coming back?
- What it tests
- Reproducing a rare hang with a stress loop and a timeout, reasoning about the window between a check and a wait, deterministic test hooks, and how many clean runs prove a fix.
- First step
- Write a stress test that shuts the queue down thousands of times and treats a shutdown that takes over 100 ms as a hang, so the bug shows in seconds. Run it under ThreadSanitizer too: if it stays silent while the test still hangs, the bug is probably not a data race, which points to the order of operations around the flag.
- Follow-ups
-
- Why does making the flag atomic not help?
- How do you make the test fail every time?
- How many clean runs do you need before you trust the fix?
From the lesson Concurrency-bug hunt: races and deadlocks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A concurrency test fails in CI about once in 300 runs and nobody can reproduce it locally. What do you do, step by step, and how do you decide that a fix worked?
- What it tests
- Turning a flaky test into a reproducible one, the arithmetic of rare failures, sanitizer runs, deterministic hooks, and how much confidence clean runs give after a fix.
- First step
- Collect the failing logs and the exact build options, then loop the test with those options on a machine like CI's, with more threads than cores, until it fails. Work out from the failure rate how many runs that should take, and reproduce it before forming a theory.
- Follow-ups
-
- Why might it never fail on a developer's laptop?
- When is adding a sleep a good idea, and when does it hide the bug?
- How many clean runs after the fix are enough?
From the lesson Concurrency-bug hunt: races and deadlocks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A production process stops responding: the processor is idle and requests time out, and you may keep it alive for a few minutes. What do you collect, how do you recognise a lock-order deadlock in it, and how do you fix and prevent it?
- What it tests
- Capturing every thread's stack from a live process, reading a lock cycle from the stacks, telling a deadlock from slowness, and fixing it and keeping it from coming back.
- First step
- Capture every thread's stack before restarting, with a debugger or a sampler. Then, for each thread blocked in a mutex lock, work out from its frames which locks it already holds and which one it waits for.
- Follow-ups
-
- How do you tell a deadlock from a slow dependency in the stacks?
- How does std::scoped_lock avoid the deadlock?
- How would you stop this class of bug from coming back?
From the lesson Concurrency-bug hunt: races and deadlocks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes