I’ve a function that accesses(reads and writes to) a std::atomic<bool>
variable. I’m trying to understand the order of execution of instructions so as to decide whether atomic will suffice or will I’ve to use mutexes here. The function is given below –
// somewhere member var 'executing' is defined as std::atomic<bool>` int A::something(){ int result = 0; // my intention is only one thread should enter next block // others should just return 0 if(!executing){ executing = true; ... // do some really long processing ... result = processed; executing = false; } return result; }
I’ve read this page on cppreference which mentions –
Each instantiation and full specialization of the std::atomic template defines an atomic type. If one thread writes to an atomic object while another thread reads from it, the behavior is well-defined (see memory model for details on data races)
and on Memory model page the following is mentioned –
When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race unless either
both conflicting evaluations are atomic operations (see std::atomic)
one of the conflicting evaluations happens-before another (see std::memory_order)
If a data race occurs, the behavior of the program is undefined.
and slight below it reads –
When a thread reads a value from a memory location, it may see the initial value, the value written in the same thread, or the value written in another thread. See std::memory_order for details on the order in which writes made from threads become visible to other threads.
This is slightly confusing to me, which one of above 3 statements are actually happening here?
When I perform if(!executing){
is this instruction an atomic instruction here? and more important – is it guaranteed that no other thread will enter that if loop if one two threads will enter that if body since first one will set executing
to true
?
And if something’s wrong with the mentioned code, how should I rewrite it so that it reflects original intention..
Advertisement
Answer
If I understand correctly, you are trying to ensure that only one thread will ever execute a stretch of code at the same time. This is exactly what a mutex does. Since you mentioned that you don’t want threads to block if the mutex is not available, you probably want to take a look at the try_lock()
method of std::mutex
. See the documentation of std::mutex.
Now to why your code does not work as intended: Simplifying a little, std::atomic guarantees that there will be no data races when accessing the variable concurrently. I.e. there is a well defined read-write order. This doesn’t suffice for what you are trying to do. Just imagine the if branch:
if(!executing) { executing = true;
Remember, only the read-write operations on executing
are atomic. This leaves at least the negation !
and the if
itself unsynchronized. With two threads, the execution order could be like this:
- Thread 1 reads
executing
(atomically), value is false - Thread 1 negates the value read from
executing
, value = true - Thread 1 evaluates the condition and enters the branch
- Thread 2 reads
executing
(atomically), value is false - Thread 1 set
executing
to true - Thread 2 negates the value, which was read as false and is now true again
- Thread 2 enters the branch…
Now both threads have entered the branch.
I would suggest something along these lines:
std::mutex myMutex; int A::something(){ int result = 0; // my intention is only one thread should enter next block // others should just return 0 if(myMutex.try_lock()){ ... // do some really long processing ... result = processed; myMutex.unlock(); } return result; }