Does std :: atomic_flag in getter / setter wrap its nullity?

Let's say that I have a class that contains std::atomic_flag as a private member opened via getter. Something like the following (pseudo-code):

 class Thing { private: std::atomic_flag ready = ATOMIC_FLAG_INIT; public: isReady() { return ready.test_and_set(); } } 

My naive question is: does the checkbox through the method ask to turn it into a non-atomic operation, being a function call non-atomic (or is it?)? Should I make my ready flag an open member and request it directly?

+5
source share
3 answers

No, it is not. The test_and_set() operation itself is atomic, so it doesn’t matter how deep the call stacks of different threads are.

To demonstrate this, consider the basic case where the atomic_flag object atomic_flag "open":

 static atomic_flag flag = ATOMIC_FLAG_INIT; void threadMethod() { bool wasFirst = !flag.test_and_set(); if( wasFirst ) cout << "I am thread " << this_thread::get_id() << ", I was first!" << endl; else cout << "I am thread " << this_thread::get_id() << ", I'm the runner-up" << endl; } 

If two threads enter threadMethod - one thread ( t1 ) a bit before the other ( t2 ), then we can expect the console output to be as follows (in the same order):

 I am thread t1, I was first! I am thread t2, I'm the runner-up 

Now, if both threads enter at the same time, but t2 is the microsecond ahead of t1 , but t2 then becomes slower than t1 , since it writes to stdout, then the output will be:

 I am thread t1, I'm the runner-up I am thread t2, I was first! 

... therefore the call to test_and_set was still atomic, although the output is not necessarily in the expected order.

Now, if you had to wrap the flag another way (not built-in to be sure), like this ...

 __declspec(noinline) bool wrap() { return !flag.test_and_set(); } void threadMethod() { bool wasFirst = wrap(); if( wasFirst ) cout << "I am thread " << this_thread::get_id() << ", I was first!" << endl; else cout << "I am thread " << this_thread::get_id() << ", I'm the runner-up" << endl; } 

... then the program will not behave differently - because the value false or true return bool from test_and_set() will still be in each thread stream. Ergo, wrapping a atomic_flag does not change its atomicity.

+6
source

The atomicity property of C ++ atomicity ensures that the operation cannot be split in the middle. That is, for the second stream observing the atom, it will either observe the state before test_and_set , or the state after test_and_set . It is not possible to sneak such a stream into clear between the test and set .

However, this is true only for the operation itself. As soon as the test_and_set call test_and_set completed, all bets are disconnected again. You should always assume that the thread executing test_and_set may be previously skipped right after the completion of this command, therefore you cannot assume that any instruction executed after test_and_set will still observe the same state.

Thus, adding a function call does not cause you any trouble. Any instruction following an atomic instruction should assume that the state of the atomic variable could be changed in the meantime. This takes into account Atomics, providing interfaces that are designed in a special way: for example, test_and_set returns the result of the test, because receiving this information with a separate call will no longer be atomic.

+4
source

No, the isReady() method will work just like a direct call to test_and_set() , i.e. atomically.

+1
source

Source: https://habr.com/ru/post/1268388/


All Articles