Racing data in a simple spinlock using atomic <bool>

#include<atomic>
#include<thread>
#include<vector>
#include<iostream>
#include<algorithm>
#include<mutex>

using namespace std;

class spinlock
{
    private:
        atomic<bool> flag;
    public:
        spinlock():flag(false)
        {
        }

        void lock()
        {
            bool temp=false;
            while(!flag.compare_exchange_weak(temp, true, std::memory_order_seq_cst) && !temp);
        }

        void unlock()
        {
            flag.store(false, std::memory_order_seq_cst);
        }
};

int main()
{
    spinlock sp;    
    //mutex sp; 
    std::vector<std::thread> threads;
    int count=0;
    std::cout << "increase global counter with 10 threads...\n";
    for (int i=1; i<=10000; ++i)
    {
        threads.push_back(std::thread([&sp,&count](){for(int i=0;i<10;++i){sp.lock();++count;sp.unlock();}}));
    }

    for_each(threads.begin(),threads.end(),[](thread &t){t.join();});

    cout<<count<<endl;
    return 0;
}

The above code creates 10k streams and uses them to increment the counter. I run it on an x86_64 machine with gcc 5.2, I tried code with different memory orders and compare_exchange_weak / strong. None of them expected a result of 100 thousand. When I perform the same experiment with a mutex, I get the correct result. Why don't I get the correct result here if the atomic atom is really atomic and the memory ordering seq_cst is the strongest available here that I used here?

Edit (fixed code):

void lock()
{
    bool temp=false;
    while(!flag.compare_exchange_weak(temp, true, std::memory_order_seq_cst))
   {
        temp=false;
   }
}

- , temp- , temp , . .

+4
1

compare_exchange_weak. , temp false, temp . . () compare-exchange-weak

, pause.

+1

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


All Articles