Back to Basics: Concurrency - Arthur O'Dwyer - CppCon 2020

CppCon · 120K views · 2020 · 1:04:28

Open on YouTube

C, C++, Rust, Go & systems languagesSoftware practice, open source & careerstalk

Why Stack Overflow recommends it

Stack Overflow users link it as a reference for the blue/green atomic shared_ptr pattern, correct condition variable and mutex use, and why busy-wait loops can be optimized by the compiler.

Recommended by 4 people across 4 different months, 2019 to 2024.

What it is

A CppCon 2020 'Back to Basics' talk by Arthur O'Dwyer on C++ threading. It covers std::thread, mutexes, condition variables, the C++20 semaphore, latch and barrier, and the 'blue/green' pattern for taming shared mutable state, plus std::future and std::async.

What people said

Recommends unDeadHerbs · 2019-12 · Stack Overflow · on “Why is most uses of volatile deprecated in C++20?”

There's a good talk by the C++ committee language evolution chair [LINK] on why. Brief summary, the places that volatile is being removed from didn't have any well defined meaning in the standard and just caused confusion. Motivating (Ambiguous) Examples Volatile bit Fields should be specified by your hardware manual and/or compiler. Is += a single/atomic instruction? How about ++ ? How many reads/writes are needed for compare_exchange ? What if it fails? What does void foo(int volatile n) mean? or int volatile foo() ? Should *vp; do a load? (This has changed twice in the standard.) Threading Historically, people have used volatile to achieve thread safety in C and C++. In C++11, non-UB ways

Recommends Lorah Attkins · 2021-11 · Stack Overflow · on “Explanation of the blue green pattern for C++ concurrency”

In this talk on concurrency [this video] , Arthur O'Dwyer explains the "blue/green" pattern. This looks like something I'd like to have in my toolbox but don't fully grasp it yet. Essentially it creates two categories for a shared state (namely blue and green) and has: A write side, were modifications are atomically added only when the blue state is consistent: using ConfigMap = std::map<std::string, std::string>; std::atomic<std::shared_ptr<const ConfigMap>> g_config; void setDefaultHostname(std::string const& value) { std::shared_ptr<const ConfigMap> blue = g_config.load(); do { std::shared_ptr<ConfigMap> green = std::make_shared<ConfigMap>(*blue); green->insert_or_assign("default.hostname", va

Recommends dopller · 2023-06 · Stack Overflow · on “Atomic Map Access and Modification in C++ without Locks”

I'm trying to achieve atomic access and modification to a map in C++ without using locks. I have a single writer thread, and I want to ensure that the map can be safely accessed and modified without the need for locks. I came across the blue-green pattern mentioned in Arthur O'Dwyer's CppCon talk ( [this video] ), which seems like a promising approach. However, I'm facing some issues with my implementation, and I would appreciate some guidance from the community. Here's a simplified version of my code: #include <iostream> #include <map> #include <string> #include <atomic> #include <memory> struct Config { using ConfigMap = std::map<std::string, std::string>; std::atomic<std::shared_ptr<const Conf

Recommends PaulH · 2023-10 · Stack Overflow · on “unlock the mutex after condition_variable::notify_all() or before?”

I am new to C++ threading and had to do some research online for using condition variables. I found the discussion between @DavidSchwartz and @DannyNiu interesting. Since I was not allowed to add a comment to that discussion, I am just offering here another relevant reference from this CppCon video @36:18. Back to Basics: Concurrency - Arthur O'Dwyer - CppCon 2020 [this video] void returnToken(Token t) { std::unique_lock lk(mtx_); tokens_.push_back(t); lk.unlock(); cv_.notify_one(); } Here Arthur O'Dwyer says: Why isn't it a lock guard? It actually could be a lock guard. The reason I made it a unique lock is so that I could manually unlock it here before doing the notify. In previous presentation

Cites Ashot · 2021-08 · Stack Overflow · on “Why is the compiler allowed to optimize out this busy waiting loop?”

#include <iostream> #include <thread> #include <mutex> int main() { std::atomic<bool> ready = false; std::thread threadB = std::thread([&]() { while (!ready) {} printf("Hello from B\n"); }); std::this_thread::sleep_for(std::chrono::seconds(1)); printf("Hello from A\n"); ready = true; threadB.join(); printf("Hello again from A\n"); } This is an example from the CppCon talk [this video] (min 17) The objective is to first print Hello from A then allow threadB to start. It is clear that busy waiting should be avoided because it uses a lot of CPU. The author said that the while (!ready) {} loop can be optimized (by putting the value of ready into a register) by the compiler because the compiler sees t

Cites Sadless Kao · 2022-04 · Stack Overflow · on “Why does the spin check while(condition){} not avoid the std::condition_variable::wait(lock, condition)?”

@ 273K I have a guess. I am thinking about `atom_prev_iThrID`. `atom_prev_iThrID` is modified in the lock. If we want to read `atom_prev_iThrID` correctly, we should use lock, too. However, the while spin is not in a lock, so it is possible to get a wrong `atom_prev_iThrID`, even `atom_prev_iThrID` is atomic. My idea is from here [this video] .

Cites Dudly01 · 2023-01 · Stack Overflow · on “Thread pool with job queue gets stuck”

I want to split jobs among multiple std::thread workers and continue once they are all done. To do so, I implemented a thread pool class mainly based on this SO answer [LINK] . I noticed, however, that my benchmarks can get stuck, running forever, without any errors thrown. I wrote a minimal reproducing code, enclosed at the end. Based on terminal output, the issue seems to occur when the jobs are being queued. I checked videos ( 1 [this video] , 2 [LINK] ), documentation ( 3 [LINK] ) and blog posts ( 4 [LINK] ). I tried replacing the type of the locks, using atomics. I could not find the underlying cause. Here is the snippet to replicate the issue. The program repeatedly counts the odd elements

Cites DDG · 2024-02 · Stack Overflow · on “busy waiting with atomic<bool>”

Can compiler hoist out checks to a atomic variable in a tight infinite loop? std::atomic<bool> ready = false; void run() { while(!ready){} // ... do something } Does the compiler have the freedom to rearrange the above code to this std::atomic<bool> ready = false; void run() { bool is_ready = ready; while(!is_ready){} // ... do something } [this video] at 17:24, Arthur O'Dwyer says compiler can rearrange. It seems quite counter intuitive. What if I change the call to ready.load(), would it prevent the compiler from rearranging the code? If not, then how come CAS operations work with in a while loop. Can't compiler rearrange those in a similar fashion?