Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Acquire release operation of c++11(atomic)

#include <atomic>
#include <iostream>
#include <thread>

class atomicAcquireRelease00
{
public:
    atomicAcquireRelease00() : x(false), y(false), z(0) {}
    void run()
    {
        std::thread a(&atomicAcquireRelease00::write_x, this);
        std::thread b(&atomicAcquireRelease00::write_y, this);
        std::thread c(&atomicAcquireRelease00::read_x_then_y, this);
        std::thread d(&atomicAcquireRelease00::read_y_then_x, this);

        a.join();
        b.join();
        c.join();
        d.join();

        std::cout<<"z == "<<z.load()<<std::endl;
    }

private:
    void write_x()
    {                
        x.store(true, std::memory_order_release); //(1)
    }

    void write_y()
    {           
        y.store(true, std::memory_order_release); //(2)
    }

    void read_x_then_y()
    {            
        while(!x.load(std::memory_order_acquire)); //(3)
        if(y.load(std::memory_order_acquire)){    //(4)
            ++z;
        }
    }

    void read_y_then_x()
    {           
        while(!y.load(std::memory_order_acquire));  //(5)
        if(x.load(std::memory_order_acquire)){      //(6)
            ++z;
        }

    }

private:
    std::atomic<bool> x, y;
    std::atomic<int> z;
};            

int main()
{
   for(size_t i = 0; i != 50; ++i){
     atomicAcquireRelease00().run();
   }          

  return 0;
}

atomicAcquireRelease00 do not respect the order when loading the value. As far as I know, if I declared the operation store as std::memory_order_release and operation load as std::memory_order_acquire as a pair, the operations of load and store on the same atomic variable will synchronize, but this simple example doesn't work as I expected.

The process base on my imagination

  • case A :

    1. x set as true
    2. at (4), y.load return false, z remain zero
    3. y set as true
    4. after (6), z become one
  • case B :

    1. y set as true
    2. at (6), x.load return false, z remain zero
    3. x set as true
    4. after (4), z become one
  • case C :

    1. x set as true, y set as true
    2. after (4) and (6), z become 2

I can't guarantee x or y would be set to true first, but when x set to true, the load of x should be synchronized with it as well as y, so what kind of situation would make z remain zero?

like image 938
StereoMatching Avatar asked Sep 07 '13 19:09

StereoMatching


Video Answer


2 Answers

This is exactly the Sample Listing 5.7 from "Concurrency In Action" by Anthony Williams.

He explains:

In this case the assert can fire (just like in the relaxed-ordering case), because it’s possible for both the load of x and the load of y to read false. x and y are written by different threads, so the ordering from the release to the acquire in each case has no effect on the operations in the other threads.

enter image description here

like image 104
sehe Avatar answered Oct 30 '22 19:10

sehe


C/C++11 does not guarantee a total order of stores to different memory locations unless you use seq_cst. So each thread is free to see the stores in a different order. Acquire-release synchronization doesn't help as it is only from the thread doing the store to the thread doing the load. If you want to play around with unit tests like this and better develop your intuition, try CDSChecker. It is a tool that will show you all behaviors that any real implementation of C11 is likely to produce.

like image 21
briand Avatar answered Oct 30 '22 17:10

briand