From what I could gather in C++ online documentation, assigning to a joined std::thread object should call its destructor and represents a legitimate operation. Is this the case?
Here some example to show what I mean:
#include <thread>
#include <vector>
using namespace std;
int main()
{
vector<thread> tvec;
for(int = 0; i < 3; ++i)
{
tvec.push_back(thread(foo));
}
for(size_t i = 0; i < 3; ++i)
{
tvec[i].join();
tvec[i] = thread(foo); // is this ok?
}
for(auto& t : tvec)
{
t.join();
}
}
assigning to a joined std::thread object should call its destructor
No it shouldn't! Destructors are only called when objects are destroyed, hence the name.
and represents a legitimate operation
It's fine as long as the thread is not joinable (as is the case in your example). Otherwise, terminate will be called.
If you were to read the standard, rather than dubious online "documentation", you'd find in [thread.thread.assign]
Effects: If joinable(), calls terminate(). Otherwise, assigns the state of x to *this and sets x to a default constructed state.
Calling the assignment operator on a thread checks to see if the thread is joinable() if it is then std::terminate() is called. If it is not then it assigns the state of the thread on the right hand side to the thread being assigned to. It then leaves the thread on the right hand side in a default constructed state.
This is called a move assignment. The actual assignment operator is deleted.
Related
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.
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.
I want to initialize a field in a constructor and never change it afterwards. I want the guarantee that after the constructor finished, every read of the field reads the initialized value, no matter in which thread the read happens.
Basically, I want the same guarantees as a final field gives in Java.
This is what I tried:
#include <atomic>
#include <iostream>
#include <thread>
struct Foo
{
Foo(int x) : x(x)
{
// ensure all writes are visible to other threads
std::atomic_thread_fence(std::memory_order_release);
}
int x;
};
void print_x(Foo const& foo)
{
// I don't think I need an aquire fence here, because the object is
// newly constructed, so there cannot be any stale reads.
std::cout << foo.x << std::endl;
}
int main()
{
Foo foo(1);
std::thread t(print_x, foo);
t.join();
}
Is this guaranteed to always print 1 or can thread t observe foo.x in an uninitialized state?
What if instead of using the member initializer x(x) an explicit assignment this->x = x; is used?
What if x is not an int but some class type?
Does making x a const int change anything with regards to thread safety?
Basically, if everything else is correct, there shouldn't be any
problem. After initializing the field, and before accessing it
in any thread, you need some sort of memory synchronization;
that's clear. Otherwise, how can the other threads know that it
is constructed. If you initialize it before starting the other
threads, then creating the threads will ensure the necessary
synchronization. (This only holds between the thread doing the
creation, and the created thread. Other already running threads
are not synchronization.) After that, as long as no thread
modifies the value, no synchronization is needed.
With regards to your code, you don't need the fence, because the
value is initialized before any of the other threads are
created, and creating the thread ensures the necessary
synchronization.
I am reading documents about c++11 multi-threads, and met this example for std::thread.
Code:
void thread_task(int n) {
...
}
int main(int argc, const char *argv[])
{
std::thread threads[5];
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
return 0;
}
I do not understand threads[i] = std::thread(thread_task, i + 1);. Is the std::thread a static function call, and returns a reference for std::thread object? Sounds inconceivable, but seems to be what the code say.
Because I would write it like this:
std::thread *threads[5];
for (int i = 0; i < 5; i++) {
threads[i] = new std::thread(thread_task, i + 1);
}
Thanks.
Let's go through exactly what is happening:
std::thread threads[5];
This creates an array of 5 std::thread objects, which are default constructed. Currently they represent "not a thread" as this is the state default construction leaves them in.
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
This uses the move form of operator=. Because threads are not copyable, only moveable, thread& operator=(thread&& t) is defined while thread& operator=(const thread& t) is not. This assigns the thread object in threads[i] to the newly constructed std::thread(thread_task, i + 1);.
There is no reason to use an array of pointers here. It adds nothing but the possibility for memory leaks.
Is the std::thread a static function call, and returns a reference for std::thread object?
No, it's creating a temporary thread object; just as int(42) creates a temporary integer. The assignment moves that into the array, since threads are movable and temporaries can be moved from.
Because I would write it like this:
You're introducing dynamic allocation, an extra level of indirection, and memory leaks (unless you also add code to delete them when you're finished) for no good reason.
The default constructor of std::thread creates an instance that does not represent a thread. The assignment is a move-assignment, since copies are also not allowed. It moves the real instance created by std::thread(thread_task, i + 1); into the array.
This:
std::thread(thread_task, i + 1)
is a call to the std::thread constructor, which creates a new thread object and passes it a pointer to the thread_task function, which itself is called with i+1 as parameter.
The assignment is a move assignment (because the right hand side refers to an anonymous object), not a copy assignment. Note that the std::thread copy constructor and copy assignment operator are deleted.
This is actually a cleaner way than using pointers, because the std::thread objects will be destructed automatically when the threads array goes out of scope.
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();
// ^^^^^^^^^^^^
}