Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with std::lock_guard

I have simple code: first thread pushes std::strings to the std::list, and second thread pops std::strings from this std::list. All std::lists operations are protected with std::mutex m. This code permanently prints error to console: "Error: lst.begin() == lst.end()".

If I replace std::lock_guard with construction m.lock() and m.unlock() the code begins work correctly. What is wrong with std::lock_guard?

#include <iostream>
#include <thread>
#include <mutex>
#include <list>
#include <string>

std::mutex m;
std::list<std::string> lst;

void f2()
{
    for (int i = 0; i < 5000; ++i)
    {
        std::lock_guard<std::mutex> { m };
        lst.push_back(std::to_string(i));
    }

    m.lock();
    lst.push_back("-1"); // last list's element
    m.unlock();
}

void f1()
{
    std::string str;

    while (true)
    {
        m.lock();
        if (!lst.empty())
        {
            if (lst.begin() == lst.end())
            {
                std::cerr << "Error: lst.begin() == lst.end()" << std::endl;
            }
            str = lst.front();
            lst.pop_front();
            m.unlock();
            if (str == "-1")
            {
                break;
            }
        }
        else
        {
            m.unlock();
            std::this_thread::yield();
        }
    }
}

// tested in MSVS2017
int main()
{
    std::thread tf2{ f2 };
    f1();
    tf2.join();
}
like image 872
Igorrr37 Avatar asked Dec 31 '18 11:12

Igorrr37


1 Answers

You did not obey CppCoreGuidelines CP.44: Remember to name your lock_guards and unique_locks :).

In

for (int i = 0; i < 5000; ++i)
{
    std::lock_guard<std::mutex> { m };
    lst.push_back(std::to_string(i));
}

you are only creating a temporary std::lock_guard object which is created and destroyed immediately. You need to name the object like in

{
    std::lock_guard<std::mutex> lg{ m };
    lst.push_back(std::to_string(i));
}

so that the lock guard lives until the end of the block.

And as you already recognized (CppCoreGuidelines):

Use RAII lock guards (lock_guard, unique_lock, shared_lock), never call mutex.lock and mutex.unlock directly (RAII)

If you are using Microsoft Visual Studio, I recommend using the code analysis and activating at least the Microsoft Native Recommended Rules. If you do this you will get a compiler analysis warning.

warning C26441: Guard objects must be named (cp.44).

like image 119
Werner Henze Avatar answered Oct 01 '22 15:10

Werner Henze