cin >> (pointer to char);

Pages: 12
What JLBorges is pointing out is the fact that, per the example I posted, there is no way to know when that second thread might change "a", and so it would be pure accident. It is called a "data race" because that second multiplication and the second thread are racing to be the next instruction to write to or read from "a".

There are mechanisms for synchronizing such activity, and when those synchronization methods are used, one still needs to inform the compiler to avoid optimizations that might skip re-reading memory.

I know what a data race is thankfully, so I did understand things by then (your example cleared things a lot).

I’m kind of curious now how atomic works because I know there’s something called "locking" but I just thought about the fact that checking whether you can access something is checking memory so shouldn’t that cause undefined behavior? Like what if I have several threads trying to lock onto the same atomic? Wouldn’t that in itself cause a data race to lock the atomic?
> checking whether you can access something is checking memory so shouldn’t that cause undefined behavior?
> Like what if I have several threads trying to lock onto the same atomic?
> Wouldn’t that in itself cause a data race to lock the atomic?

Most architectures provide support for (integral sized) atomic operations at the hardware level;
at the very least, an atomic compare_and_swap operation.
C++ requires that at least a lock-free atomic boolean type must be available.
https://en.cppreference.com/w/cpp/atomic/atomic_flag

For atomic objects of a small size, these lock-free atomic instructions can be directly used. For example:
1
2
3
4
5
6
7
8
#include <atomic>

extern int i ;
extern std::atomic<int> ai ;

void incr() { ++i ; } // add DWORD PTR i[rip], 1

void incr_a() { ++ai ; } // lock add DWORD PTR ai[rip], 1 

https://gcc.godbolt.org/z/aA9kPD


When the atomic objects are bigger, and there is no direct hardware support for atomic operations on memory blocks of that size, C++ atomic operations are implemented using locking mechanisms like mutexes.
(Note that a mutex itself could be implemented using the small lock-free atomic operations mentioned earlier.)
See: https://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free
@JLBorges has the goods there, I thought I'd just chime in with this observation.

On x86 (however many bits), the instructions are called "lock" instructions, as in "lock cmp_xchg", or something to that effect. It bends the mind to think what is termed "lock free" does factually involve locking, but the difference is whether or not source must implement a lock.

For the atomic operations, whatever locking is performed is done in hardware, and it does exact a performance penalty, varying by CPU, in the range of 18 to 65 clock cycles.

That is, however, much faster than using a mutex in *Nix or Windows, which are operating system objects with functions to be called (takes much longer).

There is also, in lock free (atomic operations), no sense of releasing a lock. Key is that the instruction, be it an increment or cmp_xchg, is performed under synchronization at the hardware level, and when that instruction is completed there is no lock to be released, as there is in the case of a mutex (or related object).
So some things do not need to have locks because they already have hardware lock things?
Topic archived. No new replies allowed.
Pages: 12