Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can inline substitution cause an infinite loop in multithreaded code?

Please note: This is just a question out of curiosity, but not about writing better-multithreaded code. I don't and won't write code like this in real projects, of course.

Inline substitution may occur when the inline keyword is added. So I'm curious.

Let's say we have code like this:

static bool done = false;

inline void setDone(bool newState) {
    done = newState;
}

inline bool getDone() {
    return done;
}

void someWorkerThreadJob() {
    // Accessing without any lock/atomic/volatile
    while (getDone() == false) {
    }
}

Can someWorkerThreadJob() be compiled like below and run into an infinite loop?

void someThreadJob() {
    while (done == false) {
    }
}

This also leads me to the next question. What about the getters and setters in classes? Member functions defined inside a class are implicitly inline, so I think inline substitution can occur, thus the same problem. Is this correct?

like image 501
Jenix Avatar asked Jan 24 '23 09:01

Jenix


1 Answers

The access to done must be protected in parallel and synchronized between threads. Otherwise, the processor or the compiler can produce/execute an incorrect sequence of instructions. Your current program is ill-formed.

The problem you are facing is that done can be cached in the L1 CPU cache (processor dependent) or the compiler could optimize the access to the done global variable (typically by putting it in a register, although many compilers do not in practice).

You need to use atomic instructions or mutexes/locks (or any synchronization mechanism) so that done can be seen by other threads when it is modified. With atomic instructions, the compiler generates proper memory fences, do not put done in a register or/and produces instructions that synchronize the communication bus (on x86_64 for example).

For more information, you can give a look to cache-coherence protocols like the MOESI and look how x86 deals with atomics instruction.

In this case, mainstream compilers (like GCC and Clang) actually optimize the code to no-op instruction (which is totally legal here regarding the C++ standard) mainly due to the static and inline keywords helping compilers. This is not the case with std::atomic.

like image 194
Jérôme Richard Avatar answered Jan 26 '23 21:01

Jérôme Richard