1

I've passed a promise as a reference to a thread. Afterwards, the promise was moved into a vector via std::move. This is causing a segmentation fault when the software is executed.

I reckon the reference in the thread is never updated after moving the promise? How can I pass the promise to the thread so that I can move it afterwards? Please see the following code example of my problem.

#include <iostream>
#include <thread>
#include <vector>
#include <future>


class Test {        
    public:
    std::thread t;
    std::promise<int> p;
    Test(std::thread&& rt, std::promise<int>&& rp) : t(std::move(rt)), p(std::move(rp)) {}
};

int main()
{
    std::vector<Test> tests;

    {
        auto p = std::promise<int>();
        std::thread t ([&p]{
            std::cout << 1;
            p.set_value(1);
        });
        tests.push_back(Test(std::move(t), std::move(p)));
    }  

    for(Test& mytest : tests)
    {
        mytest.t.join();
    }

}
Gizmo
  • 871
  • 1
  • 15
  • 38
  • 1
    Confirmed. It segfaults on my machine, too (Debian 9, GCC 6.3, Libc6/Pthread 2.4). This is curious. I agree with you. That move should be good. – thb Mar 09 '19 at 20:16
  • 2
    thread `t`'s lambda holds a reference to `p` which goes out of scope, there's no way the lambda could know where its moved to. Even if it did, storing it in a vector can cause it to move again anyway. You can opt to *not* move the promise and use another level of indirection (like a pointer). – kmdreko Mar 09 '19 at 20:31
  • 1
    @kmdreko Unlike me, I believe that you may have the right answer. I mean to try your suggestion but should not steal your credit. Feel free to post your own answer, copy and modify mine, or whatever. I would upvote. – thb Mar 09 '19 at 20:39
  • 2
    Don't pass the promise by reference anywhere. Always move it to where it is supposed to be fulfilled. The only thing you may keep in the main thread, in this case, is the associated `future`, which you should fetch before moving the promise into the worker thread. – Ext3h Mar 09 '19 at 20:42
  • 1
    And don't use `[&]` for declaring a lambda which is potentially leaving scope, that's dangerous. Only bind by value in such case. Or if you can't, like in this case because `std::promise` can't be copied, declare a r-value parameter on the anonymous function, and forward it via the variadic constructor of `std::thread`. – Ext3h Mar 09 '19 at 20:49
  • 1
    That was a good question, +1. I learned something, too. Am glad that you posted it. – thb Mar 09 '19 at 21:03

2 Answers2

3

The promise p that the lambda holds a reference to is moved from and goes out of scope. You'll need an extra level of indirection so that the promise never moves.

auto pp = std::make_unique<std::promise<int>>();
std::thread t ([p = pp.get()] { // <--- p is a promise<int>*
    std::cout << 1;
    p->set_value(1);
});

This way, the promise never moves, you just move the pointer. The lambda gets a regular non-owning pointer to the promise.

See it here.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
1

I do not have an answer to your question. At least, I have none yet. However, no other answers seem to have appeared yet and I find your question is interesting, so let's try this:

#include <iostream>
#include <thread>
#include <vector>
#include <future>
#include <memory>

class Test {        
    public:
    std::thread t;
    std::unique_ptr<std::promise<int>> pp;
    Test(std::thread&& rt, std::unique_ptr<std::promise<int>>&& rpp)
      : t(std::move(rt)), pp(std::move(rpp)) {}
};

int main()
{
    std::vector<Test> tests;

    {
        auto pp = std::make_unique<std::promise<int>>();
        std::thread t ([&pp]{
            std::cout << 1;
            pp->set_value(1);
        });
        tests.push_back(Test(std::move(t), std::move(pp)));
    }  

    for(Test& mytest : tests)
    {
        mytest.t.join();
    }
}

Do you see what I did there? I indirected ownership of the promise through a smart pointer. We know that smart pointers destruct gracefully, so the promise itself is never moved by this code, but only the pointer to the promise is moved. Yet the code still segfaults.

So are we sure that the promise is actually what is causing the segfault?

Maybe the promise is indeed causing the segfault, but now at least we have another way to attack the problem—unless you have already tried this.

thb
  • 13,796
  • 3
  • 40
  • 68
  • 1
    If you wish, as long as no better answer has appeared, compile and run the code, think about it a bit, and tell me what you conclude. I would be interested to hear. – thb Mar 09 '19 at 20:36
  • 2
    Yes, it's a dereference of null pointer that caused the segfault of your code. a live test of it is here: https://segfault.stensal.com/a/hQ4YVoalKyVqQrej – stensal Mar 09 '19 at 20:40
  • 2
    this isn't quite right since the lambda still holds a reference to `pp` which goes out of scope – kmdreko Mar 09 '19 at 20:52