手动解锁 RAII 包装器(如 st::unique_lock)的关联互斥量是否始终是 UB?

Is manually unlocking associated mutex of a RAII wrapper (like st::unique_lock) always a UB?

我想知道手动解锁与 RAII 包装器关联的互斥锁是否总是 UB。例如,如果我们在 RAII 包装器销毁 之前再次锁定它是否可以 像这样:

int i = 0;
std::mutex mx_;

void foo() {
    for (int k = 0; k < 10000; k++) {
        std::unique_lock<std::mutex> lk(mx_);
        i++;
        mx_.unlock();
        mx_.lock();
        i++;
    }
}

我的问题的原因是我正在尝试编写一个小型 RAII 包装器,将共享锁升级为 std::shared_mutex 的独占锁,我需要手动 lock/unlock std::shared_mutex 与其他锁关联,我想知道它是否是 UB。这是我的 upgrade_lock class:

template<typename Mutex>
class upgrade_lock {
public:
    using mutex_type = Mutex;

    explicit upgrade_lock(mutex_type& mx) : mxp_(&mx) {
        mxp_->unlock_shared();
        mxp_->lock();
    }

    ~upgrade_lock() {
        mxp_->unlock();
        mxp_->lock_shared();
    }

    upgrade_lock(const upgrade_lock&) = delete;
    upgrade_lock& operator=(const upgrade_lock&) = delete;

private:
    mutex_type* mxp_ = nullptr;
};

我假设这个 class 仅在线程获取共享锁时使用。至于为什么 unlock/lock 对不被另一个互斥锁保护,我认为真的没有必要。我现在可以像这样使用 class:

int i = 0;
std::shared_mutex mx_;

void goo() {
    for (int k = 0; k < 10000; k++) {
        std::shared_lock<std::shared_mutex> lk(mx_);
        if (i > 5000) {
            upgrade_lock<std::shared_mutex> lk2(mx_);
            i++;
        }
    }
}

如果你配对正确,我认为它不是 UB(并且不会出现异常,因为 @ DanielLangr 说,它会尝试解锁一个已经解锁的互斥体)


不过你可以直接传锁

template<typename lock_type>
class upgrade_lock {
public:
    upgrade_lock(lock_type& src_lock):lock(&src_lock){
        lock->unlock_shared();
        lock->mutex()->lock();
    }

    ~upgrade_lock() {
        lock->mutex()->unlock();
        lock->lock_shared();
    }

    upgrade_lock(const upgrade_lock&) = delete;
    upgrade_lock& operator=(const upgrade_lock&) = delete;

private:
    lock_type* lock;
};

(代码未经测试)