Exercise 4. Mutexes from scratch.

This exercise is supposed to drive home the proper usage of a mutex–condition-variable pair to protect access to shared data.

Exercise 4a. Find the concurrency bug!

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.

Exercise 4b (optional). Running user code under a lock

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.