1
   int data {} 
   atomic<bool> syncer {false};
   void foo()
   {
         syncer = true;  //---> I could have used a mutex here and writelock 
         data = 5; 
         syncer = false;
   }

   void foo2()
   {
       if (syncer)   // If I used a mutex I could have read lock here 
          return 0;
       return data;
   }

I always use atomics instead of mutexes. But today in a code review, I was told to use mutexes. Then I explained that atomics may not lock and this is an advantage over mutexes.

So, both my reviewer and I ended up with a question: What is the use of mutexes? Should we ban mutexes in our code guidelines and use atomics all the time? Is there a advantage of mutexes over atomics?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Kadir Erdem Demir
  • 3,531
  • 3
  • 28
  • 39
  • 4
    `foo` is not thread safe at all - you can have any number of threads call it in order to create a race condition – UnholySheep Sep 07 '22 at 08:51
  • 4
    Stop to consider how the code from `foo` and `foo2` might be 'interleaved' at runtime if `foo` and `foo2` are running on two separate threads. – G.M. Sep 07 '22 at 08:52
  • 1
    On your last points: Not all classes can be made atomic (i.e. those without trivial default c'tors). – Adrian Mole Sep 07 '22 at 08:53
  • 1
    So essentially your use of `std::atomic` doesn't achieve anything in the shown code - it might as well have been a regular `bool` and would be just as unsafe as it is now (since reading or writing a `bool` will be an atomic operation on most architectures anyway) – UnholySheep Sep 07 '22 at 08:56
  • You could write correct synchronization using atomic (but IIRC you'd need to use compare_exchange and either a loop or the ability of reporting failure). What mutexes give you is 1/ avoiding to write that yourself -- and the issues in your code show that's tricly -- 2/ interfacing with the OS scheduler so you aren't pulling. – AProgrammer Sep 07 '22 at 08:59
  • @UnholySheep I see it is not thread safe now as well if I am in foo2's else case and writing there is a data race. If I turn atomic bool true in foo2 in else case and check with an if check in foo, it is too much overhead and like implementing an mutex myself. Thans a lot for pointing out. Do you think that it is worth to write your comment as an answer or should I discard the question completely – Kadir Erdem Demir Sep 07 '22 at 09:03
  • A task switch can happen anytime and everywhere, and your code does not provide any protection for multi-threading issues with "data". Write a test program and let multiple threads doing stuff. Then compare, if all output is what you expected. Atomics only protect that one variable they are created for and nothing else. Mutexes can protect whole blocks of code. – SKCoder Sep 07 '22 at 14:08
  • 1
    You might like to become familiar with ThreadSanitizer. It is good at finding data races that you may not spot. – Nate Eldredge Sep 08 '22 at 14:11
  • You're half way to re-inventing a [SeqLock](https://stackoverflow.com/questions/54611003/implementing-64-bit-atomic-counter-with-32-bit-atomics), but this doesn't even allow `foo2` to safely *read* `data`, let alone modify it. You can of course roll your own lock with atomics, but the library `mutex` will be tuned to work well on your system, with all the tricks like x86 `pause` instructions while spin-waiting, with a fallback to OS-assisted sleep / wake. (C++20 exposes that with `wait` / `notify`, but not the spin_loop_hint part; look at other languages like Rust for the spin loop hint.) – Peter Cordes Sep 10 '22 at 02:30
  • Near duplicate of [Reading a 64 bit variable that is updated by an ISR](https://stackoverflow.com/a/71625651) – Peter Cordes Sep 10 '22 at 05:22

0 Answers0