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();
// ^^^^^^^^^^^^
}
Related
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.
I'd like to move unique_ptr to its object's method:
class Foo {
void method(std::unique_ptr<Foo>&& self) {
// this method now owns self
}
}
auto foo_p = std::make_unique<Foo>();
foo_p->method(std::move(foo_p));
This compiles, but I don't know if it is not Undefined behavior. Since I moved from the object when also calling a method on it.
Is it UB?
If it is, I could probably fix it with:
auto raw_foo_p = foo_p.get();
raw_foo_p->method(std::move(foo_p))
right?
(Optional Motivation:)
Pass the object around to extend its lifetime. It would live in a lambda until the lambda would be called asynchronously. (boost::asio)
Please, see Server::accept first and then Session::start.
You can see the original implementation used shared_ptr, but I don't see why would that be justified, since I only need one owner of my Session object.
Shared_ptr makes code more complex and it was hard for me to understand, when not familiar with shared_ptr.
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using namespace boost::system;
using namespace boost::asio;
using boost::asio::ip::tcp;
class Session /*: public std::enable_shared_from_this<Session>*/ {
public:
Session(tcp::socket socket);
void start(std::unique_ptr<Session>&& self);
private:
tcp::socket socket_;
std::string data_;
};
Session::Session(tcp::socket socket) : socket_(std::move(socket))
{}
void Session::start(std::unique_ptr<Session>&& self)
{
// original code, replaced with unique_ptr
// auto self = shared_from_this();
socket_.async_read_some(buffer(data_), [this/*, self*/, self(std::move(self))] (error_code errorCode, size_t) mutable {
if (!errorCode) {
std::cout << "received: " << data_ << std::endl;
start(std::move(self));
}
// if error code, this object gets automatically deleted as `self` enters end of the block
});
}
class Server {
public:
Server(io_context& context);
private:
tcp::acceptor acceptor_;
void accept();
};
Server::Server(io_context& context) : acceptor_(context, tcp::endpoint(tcp::v4(), 8888))
{
accept();
}
void Server::accept()
{
acceptor_.async_accept([this](error_code errorCode, tcp::socket socket) {
if (!errorCode) {
// original code, replaced with unique_ptr
// std::make_shared<Session>(std::move(socket))->start();
auto session_ptr = std::make_unique<Session>(std::move(socket));
session_ptr->start(std::move(session_ptr));
}
accept();
});
}
int main()
{
boost::asio::io_context context;
Server server(context);
context.run();
return 0;
}
compiles with:
g++ main.cpp -std=c++17 -lpthread -lboost_system
For your first code block:
std::unique_ptr<Foo>&& self is a reference and assigning it an argument std::move(foo_p), where foo_p is a named std::unique_ptr<Foo> will only bind the reference self to foo_p, meaning that self will refer to foo_p in the calling scope.
It does not create any new std::unique_ptr<Foo> to which the ownership of the managed Foo object may be transferred. No move construction or assignment happens and the Foo object is still destroyed with the destruction of foo_p in the calling scope.
Therefore there is no risk of undefined behavior in this function call itself, although you could use the reference self in a way that could cause undefined behavior in the body.
Maybe you intended to have self be a std::unique_ptr<Foo> instead of std::unique_ptr<Foo>&&. In that case self would not be a reference, but an actual object to which ownership of the managed Foo would be transferred via move construction if called with std::move(p_foo) and which would be destroyed after the function call in foo_p->method(std::move(foo_p)) together with the managed Foo.
Whether this alternative variant is in itself potentially undefined behavior depends on the C++ standard version in use.
Before C++17 the compiler was allowed to choose to evaluate the call's arguments (and the associated move construction of the parameter) before evaluating foo_p->method. This would mean, that foo_p could have already moved from when foo_p->method is evaluated, causing undefined behavior. This could be fixed similarly to how you propose to do it.
Since C++17 it is guaranteed that the postfix-expression (here foo_p->method) is evaluated before any of the arguments of the call are and therefore the call itself would not be a problem. (Still the body could cause other issues.)
In detail for the latter case:
foo_p->method is interpreted as (foo_p->operator->())->method, because std::unique_ptr offers this operator->(). (foo_p->operator->()) will resolve to a pointer to the Foo object managed by the std::unique_ptr. The last ->method resolves to a member function method of that object. In C++17 this evaluation happens before any evaluation of the arguments to method and is therefore valid, because no move from foo_p has happened yet.
Then the evaluation order of the arguments is by design unspecified. So probably A) the unique_ptr foo_p could get moved from before this as an argument would be initialized. And B) it will get moved from by the time method runs and uses the initialized this.
But A) is not a problem, since ยง 8.2.2:4, as expected:
If the function is a non-static member function, the this parameter of the function shall be initialized with a pointer to the object of the call,
(And we know this object was resolved before any argument was evaluated.)
And B) won't matter as: (another question)
the C++11 specification guarantees that transferring ownership of an object from one unique_ptr to another unique_ptr does not change the location of the object itself
For your second block:
self(std::move(self)) creates a lambda capture of type std::unique_ptr<Session> (not a reference) initialized with the reference self, which is referring to session_ptr in the lambda in accept. Via move-construction ownership of the Session object is transferred from session_ptr to the lambda's member.
The lambda is then passed to async_read_some, which will (because the lambda is not passed as non-const lvalue reference) move the lambda into internal storage, so that it can be called asynchronously later. With this move, the ownership of the Session object transfers to the boost::asio internals as well.
async_read_some returns immediately and so all local variables of start and the lambda in accept are destroyed. However the ownership of Session was already transferred and so there is no undefined behavior because of lifetime issues here.
Asynchronously the lambda's copy will be called, which may again call start, in which case the ownership of Session will be transferred to another lambda's member and the lambda with the Session ownership will again be moved to internal boost::asio storage. After the asynchronous call of the lambda, it will be destroyed by boost::asio. However at this point, again, ownership has already transferred.
The Session object is finally destroyed, when if(!errorCode) fails and the lambda with the owning std::unique_ptr<Session> is destroyed by boost::asio after its call.
Therefore I see no problem with this approach with regards to undefined behavior relating to Session's lifetime. If you are using C++17 then it would also be fine to drop the && in the std::unique_ptr<Session>&& self parameter.
My problem deals with the usage of different vectors at the same time. I know I can't expect the same vector to work in multiple threads simultaneously. I've broken down the program so it is easier to understand it. I have a ThreadClass class that has a constructor which just adds an element to the vector k and then calls a thread toCall which then outputs the size of the vector which is supposed to be one. The object of this class is created inside of a different vector inside of the main() function using vector's push_back member.
The output turns out to be 0. Sometimes I can get 1 as well. I can produce more of the number 1 if I switch to debug mode. I have tested this problem on a gnu C++17 compiler (Ubuntu 16.04) and a Visual Studio compiler (Windows 10). My question is now if this example shows that I should avoid using vectors in multi-threaded programs totally?
class ThreadClass
{
private:
std::vector<int> k;
std::thread thr;
public:
ThreadClass() {
k.push_back(27);
thr = std::thread(&ThreadClass::toCall, this);
}
void toCall() {
std::cout << k.size() << std::endl;
}
void close() {
if (thr.joinable())thr.join();
}
};
int main(){
std::vector<ThreadClass> lols;
lols.push_back(ThreadClass());
lols[0].close();
return 0;
}
The problem is that a value of type ThreadClass holds a reference to itself. Specifically, thr contains a copy of this.
When you copy or move such a value, e.g. when the temporary ThreadClass() is moved into lols, the duplicate holds a duplicate this ptr, i.e. it points to the old temporary, whose lifetime ends after the call to lols.push_back finishes.
We can replicate this problem without threads:
class Foo
{
private:
std::vector<int> k;
Foo* possibly_this;
public:
Foo() {
k.push_back(27);
possibly_this = this;
}
void toCall() {
std::cout << possibly_this->k.size() << std::endl;
}
};
int main(){
std::vector<Foo> lols;
lols.push_back(Foo{});
lols[0].toCall();
}
(For me, it prints 0 with -O0 on 7.3.1, but again, it's UB, so it could do anything on your machine.)
lols.emplace() will not help. If a std::vector resizes, then all pointers/iterators into it are invalidated. Unfortunately, you can't change the pointers stored in thr, so you're left with one solution: disable ThreadClass's copy and move constructors, like so:
//within the definition of ThreadClass
ThreadClass(ThreadClass const&) = delete;
In order to place ThreadClass in a container, you will need an additional level of indirection to allow the actual object of a value of type ThreadClass to have a stable location. Either std::list<ThreadClass> or std::vector<std::unique_ptr<ThreadClass>> will do the trick.
One issue is that your thread can call toCall before the constructor returns. It's not a good idea to go creating threads that call back into the object in the constructor. Defer the thread creation to some kind of start or launch function and call that after the constructor returns.
This is also a problem:
lols.push_back(ThreadClass());
Here, the destructor (of the temporary) can even run before toCall gets called! That's definitely not going to work. That's another really good reason not to create a thread in a constructor -- it makes temporary objects disastrous.
I've written a super simple thread wrapper that takes a function and runs it in a thread and provides a simple mechanism to signal the thread when it's time to quit. The start function looks like
//tw.hpp
class ThreadWrapper
{
public:
// ...snipped ...
template<typename... Args>
bool start(Args&& ... args)
{
ft_ = std::async(std::launch::async, std::forward<Args>(args)... );
return true;
}
};
When I use it for a non-member function, I need to pass in a const ref of the wrapper into the function that is running to provide a handle the function can use to know when to quit:
void lone_worker(const ThreadWrapper& tw)
{
while (!tw.is_quit_requested())
{
std::cout << "working hard alone\n";
sleep(1);
}
}
void nonmember_demo()
{
ThreadWrapper tw;
tw.start(&lone_worker, std::cref(tw)); // the cref is need to avoid hundreds of lines of compiler template puke with no useful error messages
sleep(5);
std::cout << "quitting\n";
tw.request_quit();
}
I was caught off guard when I originally compiled it without using std::cref by literally hundreds of lines of compiler template puke (gcc 8.1.0) and no clear reason. Is there something I haven't done right with the perfect forwarding to require the use of cref? I assume it's partially caused by the class being non-copyable (it contains a std::future), which smells a little since at least I assume no copy should be made in the first place.
Full exampl here: https://coliru.stacked-crooked.com/a/0eb4d6160b44764a
which smells a little since at least I assume no copy should be made in the first place
You assume incorrectly. async mostly just forwards to thread, which starts by executing:
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
This does copy all the arguments. The point of the reference wrapper is to avoid this copy - instead of copying the ThreadWrapper object (which is noncopyable), you're copying a std::reference_wrapper<ThreadWrapper const> (which is copyable).
From the linked cppreference page for thread:
The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).
I am trying to execute an object's method in a C++ thread.
I am able to do it, by passing the method's address and the object (or the object's address, or std::ref(my_obj)) to the thread's constructor.
I observed that if I pass the object, rather than the object's address or std::ref(my_obj), then the object gets copied twice (I'm printing some info in the copy constructor to see that).
Here is the code:
class Warrior{
string _name;
public:
// constructor
Warrior(string name): _name(name) {}
// copy constructor (prints every time the object is copied)
Warrior(const Warrior & other): _name("Copied " + other._name){
cout << "Copying warrior: \"" << other._name;
cout << "\" into : \"" << _name << "\"" << endl;
}
void attack(int damage){
cout << _name << " is attacking for " << damage << "!" << endl;
}
};
int main(){
Warrior conan("Conan");
// run conan.attack(5) in a separate thread
thread t(&Warrior::attack, conan, 5);
t.join(); // wait for thread to finish
}
The output I get in this case is
Copying warrior: "Conan" into : "Copied Conan"
Copying warrior: "Copied Conan" into : "Copied Copied Conan"
Copied Copied Conan is attacking for 5!
While if I simply pass &conan or std::ref(conan) as a second argument to thread t(...) (instead of passing conan), the output is just:
Conan is attacking for 5!
I have 4 doubts:
Why is that I have 2 copies of the object instead of 1?
I was expecting that by passing the instance of the object to the thread's constructor, the object would get copied once in the thread's own stack, and then the attack() method would be called on that copy.
What is the exact reason why the thread's constructor can accept an object, an address, or a std::ref? Is it using this version of the constructor (which I admit I do not fully understand)
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
in all 3 cases?
If we exclude the first case (since it's inefficient), what should I use between &conan and std::ref(conan)?
Is this somehow related to the syntax required by std::bind?
Why is that I have 2 copies of the object instead of 1?
When you spin up a thread the parameters are copied into the thread object. Those parameters are then copied into the actual thread that gets created, so you have two copies. This is why you have to use std::ref when you want to pass parameter that the function takes by reference.
What is the exact reason why the thread's constructor can accept an object, an address, or a std::ref? Is it using this version of the constructor (which I admit I do not fully understand)
std::thread basically starts the new thread with a call like
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
std::invoke is built to handle all different sorts of callables and one of those is when it has a member function pointer and an object, and it calls the function appropriately. It also knows about std::reference_wrapper and can handle calling a pointer to a member function on a std::reference_wrapper to an object.
If we exclude the first case (since it's inefficient), what should I use between &conan and std::ref(conan)?
This is primarily opinion based. They both essentially do the same thing, although the first version is shorter to write.
Is this somehow related to the syntax required by std::bind?
Kind of. std::bind's operator() is also implemented using std::invoke so they have a very common interface.
All of that said you can use a lambda to give yourself a common interface.
thread t(&Warrior::attack, conan, 5);
can be rewritten as
thread t([&](){ return conan.attack(5); });
And you can use this form for pretty much any other function you want to call. I find it is easier to parse when seeing a lambda.