Search code examples
c++boostlockingmutexrecursive-mutex

Why boost::recursive_mutex is not working as expected?


I have a custom class that uses boost mutexes and locks like this (only relevant parts):

template<class T> class FFTBuf
{
    public:
        FFTBuf(); 
        [...]
        void lock();
        void unlock();
    private:
        T *_dst;
        int _siglen;
        int _processed_sums;
        int _expected_sums;
        int _assigned_sources;
        bool _written;
        boost::recursive_mutex _mut;
        boost::unique_lock<boost::recursive_mutex> _lock;
};

template<class T> FFTBuf<T>::FFTBuf() : _dst(NULL), _siglen(0),
    _expected_sums(1), _processed_sums(0), _assigned_sources(0),
    _written(false), _lock(_mut, boost::defer_lock_t())
{
}

template<class T> void FFTBuf<T>::lock()
{
    std::cerr << "Locking" << std::endl;
    _lock.lock();
    std::cerr << "Locked" << std::endl;
}

template<class T> void FFTBuf<T>::unlock()
{
    std::cerr << "Unlocking" << std::endl;
    _lock.unlock();
}

If I try to lock more than once the object from the same thread, I get an exception (lock_error):

#include "fft_buf.hpp"

int main( void ) {
    FFTBuf<int> b( 256 );
    b.lock();
    b.lock();
    b.unlock();
    b.unlock();

    return 0;
}

This is the output:

sb@dex $ ./src/test
Locking
Locked
Locking
terminate called after throwing an instance of 'boost::lock_error'
   what(): boost::lock_error
zsh: abort    ./src/test

Why is this happening? Am I understanding some concept incorrectly?


Solution

  • Try this:

    template<class T> void FFTBuf<T>::lock()
    {
        std::cerr << "Locking" << std::endl;
         _mut.lock();
        std::cerr << "Locked" << std::endl;
    }
    
    template<class T> void FFTBuf<T>::unlock()
    {
        std::cerr << "Unlocking" << std::endl;
        _mut.unlock();
    }
    

    You use the same instance of unique_lock _lock twice and this is a problem. You either have to directly use methods lock () and unock() of the recursive mutex or use two different instances of unique_lock like foe example _lock and _lock_2;.

    Update

    I would like to add that your class has public methods lock() and unlock() and from my point of view in a real program it is a bad idea. Also having unique_lock as a member of class in a real program must be often a bad idea.