Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destroying condition_variable while waiting

First my code to make my explanation more clear:

struct Foo {
    std::condition_variable cv;
};
static Foo* foo; // dynamically created object

// Thread1
foo->cv.wait(...);


// Thread2
foo->cv.notify();
delete foo; // thread1 might have not left the wait function yet

I am trying to delete a std::condition_variable while it is in wait. So from my understanding, I have to notify it first to make it leave it's waiting for routine, and then I can delete it. But after calling notify* I can't delete it right away, because it might still be in wait because it needs a few cycles. What is a common way to achieve this?

like image 468
Daniel Stephens Avatar asked Sep 12 '18 15:09

Daniel Stephens


People also ask

When would you want to use a condition variable?

Condition variables are used to wait until a particular condition predicate becomes true. This condition predicate is set by another thread, usually the one that signals the condition.

What is std :: Condition_variable?

std::condition_variable The condition_variable class is a synchronization primitive used with a std::mutex to block one or more threads until another thread both modifies a shared variable (the condition) and notifies the condition_variable .

How condition variable works?

Condition variables are synchronization primitives that enable threads to wait until a particular condition occurs. Condition variables are user-mode objects that cannot be shared across processes. Condition variables enable threads to atomically release a lock and enter the sleeping state.

Why condition variable needs mutex?

Condition variables are associated with a mutex because it is the only way it can avoid the race that it is designed to avoid.


1 Answers

You can delete it right away.

Quote from C++ standard:

~ condition_variable();

Requires: There shall be no thread blocked on *this. [Note: That is, all threads shall have been notified; they may subsequently block on the lock specified in the wait. This relaxes the usual rules, which would have required all wait calls to happen before destruction. Only the notification to unblock the wait must happen before destruction.

Basically wait functions are required to perform locking and waiting atomically:

The execution of notify_one and notify_all shall be atomic. The execution of wait, wait_for, and wait_until shall be performed in three atomic parts:

  1. the release of the mutex and entry into the waiting state;
  2. the unblocking of the wait; and
  3. the reacquisition of the lock.

Once notify wakes a thread, it should be considered "unblocked" and should contest the mutex.


There are similar guarantees about std::mutex: threads are not required to leave unlock before mutex is destroyed.

Quote from C++ standard:

The implementation shall provide lock and unlock operations, as described below. For purposes of determining the existence of a data race, these behave as atomic operations. The lock and unlock operations on a single mutex shall appear to occur in a single total order.

Later:

Note: After a thread A has called unlock(), releasing a mutex, it is possible for another thread B to lock the same mutex, observe that it is no longer in use, unlock it, and destroy it, before thread A appears to have returned from its unlock call.

Such guarantees are required to avoid issues like this, when mutex inside an object is used to protect object reference counter.


Note that this does not guarantee that your implementation has no bugs in this regard. In the past glibc had multiple bugs related to the destruction of synchronization objects, in particular pthread_mutex_unlock was accessing mutex before returning.

like image 176
StaceyGirl Avatar answered Sep 21 '22 20:09

StaceyGirl