One of my coworkers claims that as soon as an object's destructor invocation begins, all accesses to the object's members done by a thread (that's a member of the object itself) are UB.
This implies that calling std::thread::join
during the destructor of an object is UB if the thread is accessing any of the object's other members.
I briefly looked in the latest standard draft, under "Object Lifetime", but couldn't find something that gave me a conclusive answer.
Does the following code (on wandbox) introduce undefined behavior? What's the part of the standard that clarifies this interaction?
struct A
{
atomic<bool> x{true};
thread t;
// Capturing 'this' is part of the issue.
// The idea is that accessing 'this->x' becomes invalid as soon as '~A()' is entered.
// vvvv
A() : t([this]
{
while(x)
{
this_thread::sleep_for(chrono::milliseconds(100));
}
})
{
}
~A()
{
x = false;
t.join();
}
};
int main()
{
A a;
}
This is not undefined behavior. If we look at [class.dtor]/8 we have
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes.
which states the the non-static members of the class are destroyed after the body of the destructor is ran. That means all the members are alive in the destructor and manipulating x
and calling join
behave just like they would in a normal member function. The only difference is after the body of the destructor is ran then the members themselves will be destroyed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With