1

Can thread t2 unlock a mutex m, although the mutex was previously locked by thread t1? Can the mutex m be unlocked twice?

To illustrate these questions, I wrote the following little script:

#include <atomic>
#include <mutex>
#include <thread>
#include <iostream>

class spinlock_mutex {
    std::atomic_flag flag;

   public:
    spinlock_mutex() : flag(ATOMIC_FLAG_INIT){};
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire)) {
            std::cout << "cloud not acquire lock" << std::endl;
        }
        std::cout << "acquired lock" << std::endl;
    }
    void unlock() {
        flag.clear(std::memory_order_release);
        std::cout << "release lock" << std::endl;
    }
};

int main() {
    spinlock_mutex mutex{};
    std::lock_guard<spinlock_mutex> lock_a(mutex);
    std::thread t2([&](){mutex.unlock();});
    t2.join();
    std::cout << "t2 has unlocked the mutex" << std::endl;
}

Execution of this code prints the following:

acquired lock
release lock
t2 has unlocked the mutex
release lock

It seems to me that the main thread acquired the mutex via the lock_guard facility. Then, thread t2 unlocks the mutex. At the end of the main scope, the destructor of lock_guard unlocks the lock (shouldn't that been unlocked by now?) again. (When I replace the custom spinlock_mutex with std::mutex the program runs without any complaints but of course does not print any information).

So I wonder if thread t2 can indeed unlock a mutex it has not locked, and which mutex does the main thread unlock at the end? cppreference warns of undefined behavior in the description of the unlock function, and I wonder if the program documents this?

fabian
  • 1,413
  • 1
  • 13
  • 24
  • _"..warns of undefined behavior in the description of the unlock function, and I wonder if the program documents this?..."_ all you can so with Undefined Behaviour is to fix the program to remove it. – Richard Critten Jun 18 '21 at 14:12
  • "Well, he thinks he's got control of that resource, but I'm going to use it anyway and not tell him that I did anything to it." Recipe for disaster. – Pete Becker Jun 18 '21 at 14:45

2 Answers2

1

No, this is not allowed.

std::mutex::unlock

The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined.

273K
  • 29,503
  • 10
  • 41
  • 64
1

The link you shared already has the answer

The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined.

"Undefined behavior" in C++ doesn't mean "the program can define it". It literally means "this behavior will never be defined by any conforming compiler". See this question for more details on the terminology.

Specifically, the standard (§32.5.4.2.1) has this to say

The expression m.unlock() is well-formed and has the following semantics:

Preconditions: The calling thread owns the mutex.

And §16.3.2.4 defines a function's preconditions as

Preconditions: the conditions that the function assumes to hold whenever it is called; violation of any preconditions results in undefined behavior.

So unlocking a mutex you don't own in undefined behavior. It could work, it could throw an exception, it could ignore the call, it could summon demons out your nose.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • 1
    "Undefined behavior" doesn't mean "will never be defined by a conforming compiler". It means that the language definition doesn't tell you what the code does. Implementations are free to define that behavior and to document what they do. – Pete Becker Jun 18 '21 at 14:43