Lifetime Extension of Temporary Object in Multithreaded Context - c++

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.

Related

Start a thread with a member function should pass a object or pointer or a reference?

I'm confused about starting a thread with a member function. I know I need to pass a class object as the second parameter. But someone passed the object to the thread(), and someone passed an address, and I had tried to pass a reference. Both of them are compiling OK. So I am confused about which one is correct.
class X
{
public:
void do_lengthy_work(){
std::cout << "1:1" << std::endl;
}
};
int main(){
X my_x;
std::thread t(&X::do_lengthy_work, std::ref(my_x)); // pass reference
std::thread t(&X::do_lengthy_work, &my_x); // pass address
std::thread t(&X::do_lengthy_work, my_x); // pass object
t.join();
return 0;
}
The thread constructor begins executing the thread according to the rules of std::invoke. So all 3 of the lines of code you show will do something.
The first two lines (ref and pointer) are fine if you expect the lifetime of the object to be longer than the lifetime of the thread. As you can see from the link to std::invoke above, they are equivalent. Otherwise, the third line copies the object into the thread. This means that the original object now doesn't matter and can be destroyed, but also means that any results will not be visible in the the original object, only the copy.

Visibility of a variable across threads in c++

Given the following code, Is it guaranteed to see the latest value 4 of a ?
int a;
mutex mtx;
void f() {
unique_lock<mutex> lck(mtx);
// read(a);
// is it guarantee it will see the value 4?
}
int main() {
a = 4;
thread(f);
}
Yes, it is guaranteed:
32.4.2.2 Constructors [thread.thread.constr]
Synchronization: The completion of the invocation of the constructor
synchronizes with the beginning of the invocation of the copy of f.
In other words, the construction of std::thread itself, which occurs in the original execution thread, synchronizes with the beginning of the invocation of the thread function. Or, in other words: everything that happens before std::thread gets constructed, in the original thread, is visible in the thread function.

Is this code correctly synchronized?

I wonder if this code is fine or not:
#include <iostream>
#include <future>
struct Foo
{
Foo()
:m_a(0)
{
}
int m_a;
};
int main()
{
Foo f;
auto handle =
std::async( std::launch::async,
[](Foo* f) { std::cout << f->m_a << '\n'; } ,
&f
);
handle.get();
}
I believe m_a should be protected by a synchronization mechanism but my colleague says it is not necessary.
EDIT: To clarify my question: I am worried that the STORE operation from the constructor of Foo() happens after the LOAD operation from the other thread. I can't see what mechanism prevents the compiler from executing those instruction in this order.
EDIT: I believe an enthusiastic compiler could decide to inline the constructor, and delay the STORE operation for after the CALL operation to std::async. In which case the second thread could access m_a before it has been committed to memory.
Yes, this is correctly synchronised.
From the specification for async, C++11 30.6.8/5:
the invocation of async synchronizes with the invocation of f.
where f is the function argument to async (the lambda in your example).
Initialisation of f.m_a is sequenced before the call to async, and therefore before any access by the asynchronous function.
Furthermore,
the completion of the function f is sequenced before the shared state is made ready.
so the access must happen before the call to get() returns, and therefore before the object is destroyed.

Why is it impossible to move a variable to another std::thread

What is the reason that you can't move an object to another std::thread? There are situations where it could be useful. For example:
You create a loop that accepts incoming socket connections. It would be nice to move incoming connections to another thread that will handle the connections. You don't need the connection anymore in the accept loop. So why should you create a pointer?
A small test case:
#include <iostream>
#include <thread>
using namespace std;
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest &pointertest) {cout << "Copy";}
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
void foo(Pointertest &&pointertest)
{
}
int main()
{
Pointertest pointertest;
foo(std::move(pointertest)); //Works
thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'**
}
The std::thread constructor has to treat the arguments you give it somewhat differently from most forwarding functions.
The reason for this is due to questions of when the thread actually gets started. If the part of the function invocation that actually created the function argument gets run long after the thread object is created (which is entirely legal behavior), then the object that needs to be moved from may have long since been destroyed.
Just consider an altered version of your code:
std::thread some_func()
{
Pointertest pointertest;
thread test(foo,std::move(pointertest));
return test;
}
This is perfectly valid (the thread will be moved out of the function). However, there's a big problem. foo may not have been called yet. And since foo takes its parameter by reference, it now has a reference to a stack variable that has been destroyed.
That's bad. But even if foo took its parameter by value, it would change nothing. Because the actual movement into that parameter doesn't happen until some indeterminate time after the thread has been started. The attempt to move into the parameter will still use an rvalue reference to a stack variable that has been destroyed. Which again is bad.
Therefore, std::thread constructor does something different. It copy/moves the arguments you give it into internal storage (this is done on the current thread). Then it uses those values as arguments for the actual function call (this is done on the new thread).
According to the standard, the thread constructor should treat pass these internal variables to your functions as temporaries. The standard specifically states INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...), where the DECAY_COPY stuff happens on the original thread, while the INVOKE part happens on the new thread.
So it seems like your thread implementation isn't able to forward the non-copyable parameters through correctly. You should be able to pass a non-copyable type; the arguments are only required to be MoveConstructible.
So this would appear to be a bug in your implementation.
It is possible. Fixing the signature of your copy constructor makes it work for me:
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest const& pointertest) {cout << "Copy";}
// ^^^^^^
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
Also, do not forget to join your thread (or detach from it) before your thread object goes out of scope:
int main()
{
Pointertest pointertest;
thread test(foo, std::move(pointertest));
test.join();
// ^^^^^^^^^^^^
}

std::async won't spawn a new thread when return value is not stored

Consider I have lamba foo which just does some stuff and doesn't need to return anything.
When I do this:
std::future<T> handle = std::async(std::launch::async, foo, arg1, arg2);
Everything runs fine and the lamba will be spawned in a new thread.
However, when I don't store the std::future which the std::async returns, the foo will be run in the main thread and block it.
std::async(std::launch::async, foo, arg1, arg2);
What am I missing here?
From just::thread documentation:
If policy is std::launch::async then runs INVOKE(fff,xyz...) on its own thread. The returned std::future will become ready when this thread is complete, and will hold either the return value or exception thrown by the function invocation. The destructor of the last future object associated with the asynchronous state of the returned std::future shall block until the future is ready.
In
std::async(std::launch::async, foo, arg1, arg2);
The returned future is not assigned anywhere and its destructor blocks until foo finishes.
I would like to add a link to an article by Herb Sutter on async and ~future in which he argues that futures should never block.
Why blocking?
std::async(); returns std::future temporary object
temporary object is destroyed immediately, calling desctructor.
std::future destructor is blocking. It is bad and troublesome.
Why assigning is ok?
By assigning to a variable, the returned object is not destroyed immediately, but later, until end of scope of your calling code.
Code Example: main1 is ok. main2 and main3 are equivalently blocking the main thread.
void forever() {
while (true);
}
void main1() {
std::future<void> p = std::async(std::launch::async, forever);
std::cout << "printing" << std::endl; // can print, then forever blocking
}
void main2() {
std::async(std::launch::async, forever);
std::cout << "printing" << std::endl; // forever blocking first, cannot print
}
void main3() {
{std::future<void> p = std::async(std::launch::async, forever);}
std::cout << "printing" << std::endl; // forever blocking first, cannot print
}
Take a look at cplusplus.com
Return value of std::async
When launch::async is selected, the future returned is linked to the
end of the thread created, even if its shared state is never accessed:
in this case, its destructor synchronizes with the return of fn.
Therefore, the return value shall not be disregarded for asynchronous
behavior, even when fn returns void.