This exercise is supposed to drive home the proper usage of a mutex–condition-variable pair to protect access to shared data.
Open this wandbox
(backup). You'll see an implementation of a "ThreadSafeStack
"
that may or may not actually be thread-safe.
1. Read the definitions of push
and blocking_pop
. (Ignore the
letter X
in the margin.) Read the test case in main
; figure out
what it's testing for. Does it make sense? Run the code. Does it seem to work? (It should.)
2. Now look at the X
in the margin. In the "Compiler Options" on Wandbox, replace
-DX=
with -DX=std::this_thread::sleep_for(1ms);
and re-run the code.
It segfaults! This shows that the original code has a bug, and the bug manifests itself
only when a context-switch happens at exactly the right time.
Sidebar: This exercise used to tell you to use -DX=std::this_thread::yield();
instead of sleep_for
. But as of Clang 7, for some reason,
yield
no longer suffices to reproduce the segfault!
3. Given what you know about the proper use of mutex
and condition_variable
,
find and fix the bug. (Your fix should actually make the code shorter and simpler.) When you're
done, run the code again; it should no longer segfault, with or without the calls to
sleep_for
.
1. (Optional) This "ThreadSafeStack<T>
" is somewhat dangerous, because
lines 20, 30, and 31 all run user-supplied code under a mutex lock. Imagine, or write,
a class Widget
such that ThreadSafeStack<Widget>
would
misbehave (deadlock, segfault, etc).
2. (Optional) Browse the code for concurrency primitives in Arthur's from-scratch repo. You might particularly want to read mutex.md.
You're done with this set of exercises! Sit back and relax, or optionally, browse the following blog posts.