How to assert if a std::mutex is locked?

前端 未结 7 1542
难免孤独
难免孤独 2020-12-03 13:30

With GCC 4.8.2 (on Linux/Debian/Sid 64 bits) -or GCC 4.9 when available - in C++11- I have some mutex

std::mutex gmtx;

actually, it is

相关标签:
7条回答
  • 2020-12-03 13:47

    Strictly speaking, the question was about checking the lockedness of std::mutex directly. However, if encapsulating it in a new class is allowed, it's very easy to do so:

    class mutex :
        public std::mutex
    {
    public:
    #ifndef NDEBUG
        void lock()
        {
            std::mutex::lock();
            m_holder = std::this_thread::get_id(); 
        }
    #endif // #ifndef NDEBUG
    
    #ifndef NDEBUG
        void unlock()
        {
            m_holder = std::thread::id();
            std::mutex::unlock();
        }
    #endif // #ifndef NDEBUG
    
    #ifndef NDEBUG
        /**
        * @return true iff the mutex is locked by the caller of this method. */
        bool locked_by_caller() const
        {
            return m_holder == std::this_thread::get_id();
        }
    #endif // #ifndef NDEBUG
    
    private:
    #ifndef NDEBUG
        std::atomic<std::thread::id> m_holder;
    #endif // #ifndef NDEBUG
    };
    

    Note the following:

    1. In release mode, this has zero overhead over std::mutex except possibly for construction/destruction (which is a non-issue for mutex objects).
    2. The m_holder member is only accessed between taking the mutex and releasing it. Thus the mutex itself serves as the mutex of m_holder. With very weak assumptions on the type std::thread::id, locked_by_caller will work correctly.
    3. Other STL components, e.g., std::lock_guard are templates, so they work well with this new class.
    0 讨论(0)
  • 2020-12-03 13:52

    You could just use a recursive_mutex, which can be locked multiple times on the same thread. Note: If it were my code, I would restructure it so that I don't need a recursive_mutex, but it will address your problem.

    0 讨论(0)
  • 2020-12-03 13:52

    It's not technically an assertion, but I've used a similar approach to prevent unlocked access to shared state: add a reference parameter to the lock guard class on the in the unsafe function (beta in your example). Then the function cannot be called unless the caller has created a lock guard. It solves the problem of accidentally calling the function outside of the lock, and it does so at compile time with no races.

    So, using your example:

    typedef std::lock_guard<std::mutex> LockGuard;
    void alpha(void) {
       LockGuard g(gmtx);
       beta(g);
       // some other work
    }
    
    void beta(LockGuard&) {
       // some real work
    }
    
    void gamma(void) {
       LockGuard g(gmtx);
       beta(g);
       // some other work
    }
    
    //works recursively too
    void delta(LockGuard& g)
    {
       beta(g);
    }
    

    Drawbacks:

    • Does not validate that the lock actually wraps the correct mutex.
    • Requires the dummy parameter. In practice, I usually keep such unsafe functions private so this is not an issue.
    0 讨论(0)
  • 2020-12-03 13:53

    My solution is simple, use try_lock to test then unlock if needed:

    std::mutex mtx;
    
    bool is_locked() {
       if (mtx.try_lock()) {
          mtx.unlock();
          return false;
       }
       return true; // locked thus try_lock failed
    }
    
    0 讨论(0)
  • 2020-12-03 14:02

    Try atomic (e.g. atomic<bool> or atomic<int>), which has a nice load function that will do what you want, as well as other nice functions like compare_exchange_strong.

    0 讨论(0)
  • 2020-12-03 14:03

    std::unique_lock<L> has owns_lock member function (equivalent of is_locked as you say).

    std::mutex gmtx;
    std::unique_lock<std::mutex> glock(gmtx, std::defer_lock);
    
    void alpha(void) {
       std::lock_guard<decltype(glock)> g(glock);
       beta(void);
       // some other work
    }
    void beta(void) {
       assert(glock.owns_lock()); // or just assert(glock);
       // some real work
    }
    

    EDIT: In this solution, all lock operations should be performed via unique_lock glock not 'raw' mutex gmtx. For example, alpha member function is rewritten with lock_guard<unique_lock<mutex>> (or simply lock_guard<decltype(glock)>).

    0 讨论(0)
提交回复
热议问题