Is it safe to return std::thread from a function?
eg
std::thread getWindowThread(std::function<void()> f){
std::thread t(f);
return t;
}
std::function<void()> func = [](){};
std::thread winT = getWindowThread(func);
winT.join();
Is it safe to return std::thread from a function?
eg
std::thread getWindowThread(std::function<void()> f){
std::thread t(f);
return t;
}
std::function<void()> func = [](){};
std::thread winT = getWindowThread(func);
winT.join();
Yes, it's safe, as long as the function's return value is used to initialize or assign to another std::thread object.
The only precondition for std::thread's destructor is that joinable() is false:
~thread();
Effects: Ifjoinable(), callsterminate(). Otherwise, has no effects.
To be joinable(), the thread must have an ID not equal to a value-initialized ID:
bool joinable() const noexcept;
Returns:get_id()!=id().
And std::thread's move constructor and move-assignment operator specify that a moved-from thread will have an ID equal to a value-initalized ID, and therefore will not be joinable():
thread(thread&& x) noexcept;
Postconditions:x.get_id() == id()andget_id()returns the value ofx.get_id()prior to the start of construction.
thread& operator=(thread&& x) noexcept;
...
Postconditions:x.get_id() == id()andget_id()returns the value ofx.get_id()prior to the assignment.
...
Since getWindowThread returns a function-local variable, its return value will be move-constructed from the returned object, and since winT will be move-constructed from the value returned by getWindowThread, your example is safe. The returned thread object is no longer joinable and can be destroyed.
However, if the returned value is not used to initialize or assign to another std::thread object, its destructor will call std::terminate() and crash your program. For this reason, I would recommend adding the [[nodiscard]] attribute to your function to ensure your compiler at least issues a warning in this dangerous situation (if you have access to C++17 features, otherwise there are compiler-specific extensions to enable the same behavior):
[[nodiscard]] std::thread getWindowThread(std::function<void()> f) {
std::thread t(f);
return t;
}
Have a look std::thread's constructors. You can check that std::thread copy constructor (and copy assignment) has been deleted. See this to learn more about move constructor in C++.
When you return std::thread from the function, it's returned object is move-constructed and your variable winT will also be move-constructed as the function returns an object having value category prvalue.
std::thread winT = getWindowThread(func);
This should work and won't cause any undefined behavior. You might be thinking that once you return std::thread from the function, it will be destroyed and it's resources must be released and may be it's thread might be destroyed.
But as stated earlier, the object created in function will be move-constructed to construct winT object.
Detailed Information: This link says that:
If *this has an associated thread (joinable() == true), std::terminate() is called.
So, to safely destroyed any std::thread object, you need to make sure it is not joinable. joinable() gives more insight on this:
So a default constructed thread is not joinable. A thread that has finished executing code, but has not yet been joined is still considered an active thread of execution and is therefore joinable.
So, when move constructor of winT constructs it, it swaps itself with object returned from your function. After that, temporary of your function will be destroyed and since, it won't be joinable, it is safe to destroy it.