Search code examples
c++multithreadingboosttbb

Atomic Variables Accessed Multiple Times From One Function


I have the following code:

The header:

class Counter
{
public:
    Conuter(const std::string& fileName);
    boost::uint16_t getCounter();
private:
    tbb::atomic<boost::uint32_t> counter;
    std::string counterFileName;
};

The cpp:

Counter::Counter(const std::string& fileName) : counter(), counterFileName(fileName)
{  
    std::string line;
    std::ifstream counterFile (fileName.c_str());  
    if (counterFile.is_open())
    {
        getline (counterFile, line);
        counterFile.close();
    }

    boost::uint32_t temp = std::stoul (line,nullptr,0);
    counter = temp;
}

boost::uint32_t Counter::getCounter()
{
    if (counter > 1000)
    {
        counter = 0;
    }

    assert( counter < 1000);

    const boost::uint32_t ret = counter++;

    if ((counter % 10) == 0)
    {
        // write the counter back to the file.
        std::ofstream file (counterFileName.c_str());
        if (file.is_open())
        {
            myfile << counter;
            myfile.close();
        }
    }
    return ret;
}

And elsewhere lets say we have two threads:

boost::thread t1(&Counter::getCounter, counter);
boost::thread t2(&Counter::getCounter, counter);

My question is around the atomic variable. Since the getCounter function can access the counter value up to 3 times per call can the atomic variable change from one call to the next. For example, if the call to if (counter > 1000) fails to pass is there any sort of guarantee that the assert will also pass? Maybe more concretely does the atomic variable block until the end of the function call? Or just as long as the value is being read/written? My second question is, how does the operating system handle the atomic? As in if the atomic doesn't cause the function to block until it is finished, what happens if one thread is updating the variable and one thread is attempting to write it out? Sorry for the abundance of questions this is my first attempt at a lock free data structure.


Solution

  • First of all, even in a single-threaded app, sequence of

    if (counter > 1000) ...
    assert(counter < 1000)
    

    will fail when counter is 1000.

    Second, yes, atomic variable can change between reads easily. The whole point of it is that the single read is atomic, and if another thread is updating the variable exactly when it is read, you are guaranteed to have a proper memory-ordered read (you also have some guarantees regarding arithmetics on it - your increment is guaranteed to increment). But it says nothing about next read!

    If you need to lock the variable, you need to use locking mechansims, such as mutexes.