Suppose I have a class that may run some code asynchronously, and that asynchronous code uses that class instance to do things like call member functions, read data members, etc. Obviously the class instance must outlive the background thread in order for those accesses to be safe. It is sufficient to ensure this by joining the background thread in the destructor? For example:
#include <iostream>
#include <thread>
class foo final
{
public:
foo() = default;
void bar() {
std::cout << "Hopefully there's nothing wrong with using " << this << "\n";
}
void bar_async() {
if (!m_thread.joinable()) {
m_thread = std::thread{&foo::bar, this};
}
}
~foo() {
if (m_thread.joinable()) {
std::cout << "Waiting for " << m_thread.get_id() << "\n";
m_thread.join();
}
}
private:
std::thread m_thread;
};
int main() {
foo f;
f.bar_async();
}
Specifically, I'm worried about object lifetime rules:
For any object of class types whose destructor is not trivial, lifetime ends when the execution of the destructor begins.
... after the lifetime of an object has ended and before the storage which the object occupied is reused or released, the following uses of the glvalue expression that identifies that object are undefined: ...
- Access to a non-static data member or a call to a non-static member function.
But to me, a strict reading of the above would also imply that calling this->bar()
from inside ~foo()
directly is undefined, which is "obviously" not the case.