Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is transforming a conditional write to an unconditional write not a thread safe optimization?

In a talk about concurrency and the C++11 memory model Herb Sutter gives examples of illegal optimizations.

http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2

From the slide at minute 17:

void f(vector<widget>& v) {
    if(v.length()>0) xMutex.lock();
    for(int i = 0; i < v.length(); ++i)
        ++x;                                  // write is conditional
    if(v.length()>0) xMutex.unlock();
}

"A very likely (if deeply flawed) transformation of the central loop:"

r1 = x;
for(int i = 0; i < v.length(); ++i)
    ++r1;                                     // oops: write is not conditional
x = r1;

He explains, "...this write is not conditional, it will happen on every execution even if doOptionalWork is false, which will inject a write that is not protected by the mutex lock, that injects a race..."

Why does he say the invented write is not protected by the mutex lock? I understood the full transformation being described as the following.

// "optimized" version 1
void f(vector<widget>& v) {
    if(v.length() > 0) xMutex.lock()
    r1 = x;
    for(int i = 0; i < v.length(); ++i)
        ++r1;
    x = r1;
    if(v.length() > 0) xMutex.unlock();
}

But it could also be this.

// "optimized" version 2
void f(vector<widget>& v) {
    if(v.length() > 0) xMutex.lock()
    r1 = x;
    for(int i = 0; i < v.length(); ++i)
        ++r1;
    if(v.length() > 0) xMutex.unlock();
    x = r1;
}

Clearly version 2 isn't thread safe but I'm not sure about version 1. Is version 1 thread safe? What if there are no other lines that write to x in play?

Just now I began typing "Either v.length() is 0 or it isn't..." and realized that even tautologies fail me in a multithreaded world. I don't know where I can begin reasoning about this.

like image 941
Praxeolitic Avatar asked Mar 19 '23 04:03

Praxeolitic


1 Answers

The mutex is only used if there is something inside of the vector. Running this method concurrently on two empty vectors results in a data race because we don't lock at all, yet we write to x.

like image 59
usr Avatar answered Mar 22 '23 15:03

usr