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.thread.destr]
~thread();
Effects: If joinable(), calls terminate(). Otherwise, has no effects.
To be joinable(), the thread must have an ID not equal to a value-initialized ID:
[thread.thread.member]
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.constr]
thread(thread&& x) noexcept;
Postconditions: x.get_id() == id() and get_id() returns the value of x.get_id() prior to the start of construction.
[thread.thread.assign]
thread& operator=(thread&& x) noexcept;
...
Postconditions: x.get_id() == id() and get_id() returns the value of x.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;
}
Yes, it's safe. That thread will be moved to winT.
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.
Related
I have the following scenario:
void thread_func(const Widget& w) { /*does something here...*/ }
int main() {
Widget w{ /* some arguments */ };
std::thread t(thread_func, std::move(w));
t.detach();
// lots of other stuff down here ...
}
Now I know that temporary objects, when bound to const T& parameters in a function call have their lifetime extended until the completion of the expression in which the call occurs. But in this multithreaded context, does the "completion of the expression in which the call occurs" refer to the construction of t, or to the completion of thread_func, since thread_func is (at the very least, conceptually) called at the end of t's constructor?
In a scenario with a single thread the answer is clear:
Widget h() { return Widget{/* some arguments */}; }
void g(const Widget& x) { /* do something */ }
void f() {
g(h());
}
the lifetime of the temporary within the call to g lives until g has finished executing, since execution pauses within f, begins in g, and only resumes in f once g has finished executing. In a multithreaded environment, the answer is more confusing to me because construction happens in a single thread, but the execution directly following construction happens concurrently.
This is not a lifetime extension situation. All temporaries live until then end of the full-expression, i.e. the outermost ;.
std::thread t(thread_func, std::move(w)); - this is a full-expression. So yes, the thread is starting when the expression is already ended.
Your thread is started with a reference to the original object, which must be keep alive for as long as it's needed in the thread.
Bonus notes:
You can't move into an lvalue-reference. Your std::move has no effect. But even if you changed the function to accept an rvalue reference, it would not change the fact that the original object needs to be kept alive externally. Remember that std::move does not move anything. What would change it though is if you simply accepted it by value.
void thread_func(Widget w) { /*does something here...*/ }
Detaching a thread is almost never needed. In your example the program will end when the main thread finishes, it will not wait for any other thread to finish. Consider joining your thread before main() returns instead.
I have the following C++(11) code:
#include <mutex>
void unlock(std::unique_lock<std::mutex> && ulock)
{
}
int main(void)
{
std::mutex m;
std::unique_lock<std::mutex> ulock(m);
unlock(std::move(ulock));
if (ulock.mutex() == &m || ulock.owns_lock())
{
throw std::runtime_error("");
}
return 0;
}
What I can't figure out is why the mutex is still held after the return from unlock(). My expectation is that the std::move() causes the lock to go out of scope (and become unlocked by the destructor) upon return from the call to unlock(). At the very least, it seems like the std::move() should have caused ulock to become "unbound" from the mutex m.
What am I missing?
void unlock(std::unique_lock<std::mutex> && ulock)
Here ulock is a reference. A special kind of reference, but still a reference. It is just an alias for another object. Its creation does not involve creation of a new object, or any kind of ownership transfer. Likewise, end of its lifetime does not lead to any destructor call, it just means that you lost the alias for referring to some other object (not that it matters, since the function is ending anyway).
If you want to transfer ownership, you need an object, so pass by value instead of by reference:
void unlock(std::unique_lock<std::mutex> ulock)
Now, you will have to move the original lock, since std::unique_lock does not support copy construction, only move construction.
I am studying futures in my concurrent programming class. My professor has stated this in her slides:
"Valid" futures are future objects associated to a
shared state, and are constructed by calling one of the following functions:
async
promise::get_future
packaged_task::get_future
future objects are only useful when they
are valid. Default-constructed future objects are
not valid (unless move-assigned a valid future).
I can't understand the meaning of the above, especially the "unless move-assigned a valid future" part. Could someone please explain this in simple terms, and perhaps show some example code as well?
As stated in the std::future constructor:
Default-constructed future objects are not valid
This just means calling the default constructor for the object, something like:
std::future<int> f;
This will call constructor #1 which states:
Default constructor. Constructs a std::future with no shared state.
After construction, valid() == false.
As for the other part:
(unless move-assigned a valid future)
What is meant here is that the move constructor (future( future&& other ) #2) will be called which states:
Move constructor. Constructs a std::future with the shared state of
other using move semantics. After construction, other.valid() == false.
Basically, the state of other in this constructor is moved to this. That means if other.valid() == true then after the move constructor has returned other.valid() will be false and this.valid() will be true. If other.valid() was false to begin with then both will end up false.
std::future<int> fut; // fut.valid() == false, default constructor
std::future<int> valid_fut = std::async(std::launch::async, [](){ return 42; }); // obtain a valid std::future..
// valid_fut.valid() == true here
//now move valid_fut into new_fut
std::future<int> new_fut(std::move(valid_fut));
// new_fut.valid() == true
// valid_fut.valid() == false
To summarize:
Calling the default constructor for an std::future will result in valid() == false. Always.
Calling the move constructor for an std::future will result in valid() == true only if other.valid() was true before moving from it. False otherwise.
I have the following member function:
Person ClassB::DoSomethingAndReturnPerson()
{
RAIIMutex myLock(&m_mutex);
return m_person;
}
RAIIMutex is an helper class that recieves a mutex and locks it in the constructor and releases in the destructor.
m_person is of type Person (something very small in size). Other functions in other threads might change this member.
I want to return m_person by value (return a copy) and of course I want to avoid the situation where the m_person being changed in another thread while it's being copied in the return so I've added the lock.
But what happens first ? Does the compiler first creates a copy of m_person or first calls the destructor of myLock ?
Theoretically it easly solvable by doing something like this :
Person ClassB::DoSomethingAndReturnPerson()
{
RAIIMutex myLock(&m_mutex);
Person tmp = m_person;
return tmp;
}
But I'm interested in knowing the answer to my question.
Thanks
The copy-initialization of the returned value will be processed before.
From the standard, [stmt.return]/3 (emphasis mine)
The copy-initialization of the result of the call is sequenced before
the destruction of temporaries at the end of the full-expression
established by the operand of the return statement, which, in turn, is
sequenced before the destruction of local variables ([stmt.jump]) of
the block enclosing the return statement.
Destructors of local objects are called after 'the last line of your code'. Here is a relevant quote from Standard (3.7.3 / 3):
If a variable with automatic storage duration has initialization or a
destructor with side effects, it shall not be destroyed before the end
of its block, nor shall it be eliminated as an optimization even if it
appears to be unused, except that a class object or its copy/move may
be eliminated as specified in 12.8
Please consider the following code:
void h(M m2)
{
...
}
int main()
{
while (true) {
M m1 = ...;
std::thread t(h, std::move(m1));
t.detach();
}
}
Is it guaranteed that m2 is properly move-d constructed from m1 before m1 is destroyed? Or is there a race?
The standard seems clear to me:
Effects: Constructs an object of type thread. The new thread of execution executes INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) with the calls to
DECAY_COPY being evaluated in the constructing thread.
Since the copy is made in the calling thread it must complete before the constructor invocation returns.
Construction of m2 is done from a different object (the result of the DECAY_COPY), not from m1, so it doesn't matter whether m1 has been destroyed or not.
The result of the DECAY_COPY must be stored somewhere by the implementation so that it doesn't go out of scope until the target function has been initialized, but that is the implementation's job to get right. Destruction of m1 doesn't have any effect on it.