Today, I will finish the rules for concurrency and continue directly with lock-free programming. Yes, you have read it correctly: lock-free programming.
Before I write about lock-free programming in particular, here are the three last rules to concurrency.
- CP.43: Minimize time spent in a critical section
- CP.44: Remember to name your
- CP.50: Define a
mutextogether with the data it guards. Use
I make it short because these rules are quite obvious.
The less time your lock a mutex, the more time other threads can run. Have a look at the notification of a condition variable. If you want to see the entire program, read my previous post C++ Core Guidelines: Be Aware of the Traps of Condition Variables.
The Mutex mutex_ is locked at the beginning and unlocked at the end of the function. This is not necessary. Only the expression dataReady = true (1) has to be protected.
First, std::cout is thread-safe. The C++11 standard guarantees that each character is written in an atomic step and the correct sequence. Second, the notification condVar.notify_one() is thread-safe.
Here is the improved version of the function setDataReady:
When I teach this rule in my classes to concurrency, often a question arises: Should you document this artificial scope for limiting the lifetime of the std::lock_guard and, therefore, the lifetime of the std::mutex (line (1) and (2))? Most of my students, including me, add a comment to this artificial scope. If not, the maintainer of your code may not be aware that the curly braces in line (1) and line (2) are necessary for controlling the lifetime of the mutex. Ultimately, your maintainer will remove the artificial scope because it seems superfluous, and your code uses the first variant.
I was a little astonished to read this rule. Here is an example from the guidelines:
The unique_lock and lock_guard are just temporaries that are created and immediately destroyed. The std::lock_guard or std::unique_lock locks its mutex and its constructor and unlocks it in its destructor. This pattern is called RAII. Read the details here: Garbage Collection: No Thanks.
My small example shows only the conceptual behavior of a std::lock_guard. Its big brotherstd::unique_lock supports more operations.
MyGuard calls lock and unlock in its constructor and its destructor. Because of the temporary, the call to the constructor and destructor happens in line (1). In particular, this means that the call of the destructor happens at line (1) and not, as usual, in line (3). Consequently, the critical section in line (2) is executed without synchronization.
This execution of the program shows that the output of “unlock” happens before the output of “CRITICAL SECTION“.
The central idea is to put your mutex into the data you want to protect. With already standardized C++ it looks like this:
With an upcoming standard, it may look like this because synchronized_value<T> is not part of the current C++ standard, but it may become part of an upcoming standard.
According to proposal N4033 from Anthony Williams: “The basic idea is that
synchronized_value<T> stores a value of type
T and a mutex. It then exposes a pointer interface, such that dereferencing the pointer yields a special wrapper type that holds a lock on the mutex, and that can be implicitly converted to
T for reading, and which forwards any values assigned to the assignment operator of the underlying
T for writing.”
This means that the operation on s in the following code snippet is thread-safe.
Now as announced, to something completely different: lock-free programming.
First, let me state the most crucial meta-rule to lock-free programming.
Don’t program lock-free
Sure, you don’t believe me, but based on my experience giving many concurrency classes and workshops, this is my first rule. Honestly, I agree with many of the most appreciated C++ experts worldwide. Here are the critical statements and cites from their talks:
- Herb Sutter: Lock-free programming is like playing with knives.
- Anthony Williams: “Lock-free programming is about how to shoot yourself in the foot.“
- Tony Van Eerd: “Lock-free coding is the last thing you want to do.”
- Fedor Pikus: “Writing correct lock-free programs is even harder.“
- Harald Böhm: “The rules are not obvious.“
Here is a picture of the statements and cites:
You still don’t believe me? With C++11, the memory order std::memory_order_consume was defined. Seven years later, the official wording is: “The specification of release-consume ordering is being revised, and the use of
memory_order_consume is temporarily discouraged.” (memory_order)
If you know what you do, think about the ABA problem in the guidelines CP.100.
The following code snippet from the C++ core guidelines has a bug.
Find the bug and write me an e-mail. I will mention the best problem analysis in my next post if you like your name.
Of course, I will solve the riddle of the ABA problem in the next post. Afterward, my story with lock-free programming continues.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,