Race condition in a custom implementation of a recursive mutex

UPD . It seems that the problem that I am explaining below does not exist. I can’t play it after a week, I began to suspect that it was caused by some errors in the compiler or corrupted memory, because it can no longer be played.


I tried to implement my own recursive mutex in C ++, but for some reason it fails. I tried to debug it, but I was stuck. (I know that there is a recursive mutex in std, but I need a special implementation in a project where STL is not available, this implementation was just a test of the idea). I have not thought about efficiency yet, but I do not understand why my simple implementation does not work.

First of all, the RecursiveMutex implementation is implemented here:

class RecursiveMutex
{
    std::mutex critical_section;
    std::condition_variable cv;
    std::thread::id id;
    int recursive_calls = 0;

public:
    void lock() {
        auto thread = std::this_thread::get_id();
        std::unique_lock<std::mutex> lock(critical_section);

        cv.wait( lock, [this, thread]() {
            return id == thread || recursive_calls == 0;
        });

        ++recursive_calls;
        id = thread;
    }

    void unlock() {
        std::unique_lock<std::mutex> lock( critical_section );
        --recursive_calls;
        if( recursive_calls == 0 ) {
            lock.unlock();
            cv.notify_all();
        }
    }
};

, , ( ). :

std::vector<std::thread> threads;

void initThreads( int num_of_threads, std::function<void()> func )
{
    threads.resize( num_of_threads );
    for( auto& thread : threads )
    {
        thread = std::thread( func );
    }
}

void waitThreads()
{
    for( auto& thread : threads )
    {
        thread.join();
    }
}

void test () {
    RecursiveMutex mutex;

    while (true) {
        int count = 0;
        initThreads(2, [&mutex] () {
            for( int i = 0; i < 100000; ++i ) {
                try {
                    mutex.lock();
                    ++count;
                    mutex.unlock();
                }
                catch (...) {
                    // Extremely rarely.
                    // Exception is "Operation not permited"
                    assert(false);
                }
            }
        });

        waitThreads();
        // Happens often
        assert(count == 200000);
    }  
}

:

  • RecursiveMutex::lock(), "Operation not allowed" cv.wait. , , wait , . wait, .

  • " ".

- , , .

P.S. Desktop Qt 5.4.2 MinGW 32 .

+4

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


All Articles