Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructor in a multithreaded environment?

I was wondering what would happen in such a class:

class MyClass
{
private:
    std::vector<int> iVector;
    void Worker()
    {
        //Lots of stuff done with iVector
        //adding, removing elements, etc.
    }
}

Let's say I create a thread (invoked by one of the class member functions) that uses iVector and modifies it. Besides this worker, none of the other member functions of the class reads or modifies this std::vector.

Everything seems fine as worker thread is the only one using iVector.

But what would happen when one instance of the object is destroyed? Even if the object is destroyed after the worker thread is finished, destructor for iVector would be invoked from the main thread. Would this lead to undefined behavior?

Thanks!

like image 992
sapito Avatar asked Oct 07 '16 20:10

sapito


2 Answers

Firstly I would suggest running a std::join (or your library equivalent) on the thread in the destructor. This will ensure the thread is properly finished and synchronized before the vector destructor runs. This is important as the vector's lifetime must exceed the thread using it.

The C++11 standard and presumably later ones state in 30.3.1.5:

5: Synchronization: The completion of the thread represented by *this synchronizes with (1.10) the corresponding successful join() return. [ Note: Operations on *this are not synchronized. — end note ]

Now we have to examine 1.10 for more detail, first this section states:

3: The value of an object visible to a thread T at a particular point is the initial value of the object, a value assigned to the object by T, or a value assigned to the object by another thread, according to the rules below.

Honestly, this is difficult to parse, it does not specify exactly what kind of synchronization join offers and seems to imply it synchronizes only the thread itself and not data it has accessed. Therefore I would go the safe route and run atomic_thread_fence(memory_order_acquire) after join in the main thread, and atomic_thread_fence(memory_order_release) in the child thread just before it finishes which should guarantee full happens before semantics and no UB.

like image 111
Vality Avatar answered Oct 31 '22 23:10

Vality


If an execution thread is using the ivector class member, and another thread destroys the object with this class member, the continued use of the ivector class member results in undefined behavior.

Even if the object is destroyed after the worker thread is finished, destructor for iVector would be invoked from the main thread. Would this lead to undefined behavior?

No. As you've described, this situation is not undefined behavior. The C++ standard does not require an object to be destroyed by the same execution thread that created the object. It's fine for one execution thread to grow, resize the vector, then go away or stop using the vector, and then a different execution thread destroy the entire object.

like image 33
Sam Varshavchik Avatar answered Oct 31 '22 22:10

Sam Varshavchik