0

My code:

void job_function(std::promise<void>& p) { do_it(); p.set_value(); } void foo() { std::promise<void> p; auto* thread = new std::thread(job_function, p); p.get_future().wait_for(std::chrono::seconds(1)); } 

In this code, if the calling thread of foo only waits for 1 second until the future completes. It is quite possible that the actual job gets completed after the wait is over. In this scenario, p is destructed already so call to p.set_value will not work. I can create p on heap, but even in that case it should be deleted and which thread should delete p depends on order of job completion and wait_for. Is there a specific pattern that can be used to handle this?

2
  • The problem is that you accept the promise as reference. Meaning something other than the thread manages it's lifetime. You'd have the same issue with any other structure/class. Why don't you pass it by value? Commented Nov 9, 2021 at 7:24
  • You'll need to wait for thread to finish and delete it anyway prior to existing scope. So there will be no problem with promise lifetime. Commented Nov 9, 2021 at 7:53

3 Answers 3

3

The trick is to move the promise into the thread and only keep the future around. Also, if you don't want to wait for the thread, detach it.

void job_function(std::promise<void> p) { do_it(); p.set_value(); } void foo() { std::promise<void> p; std::future<void> f = p.get_future(); std::thread thread(job_function, std::move(p)); thread.detach(); f.wait_for(std::chrono::seconds(1)); } 
Sign up to request clarification or add additional context in comments.

Comments

1

You are reimplementing std::packaged_task. Your code could be:

void job_function() { do_it(); } void foo() { std::packaged_task<void(void)> task(job_function); std::future result = task.get_future(); std::thread task_td(std::move(task)); result.wait_for(std::chrono::seconds(1)); } 

2 Comments

although not directly mentioned in my question, I cannot use packaged_task as the job has to flow through a pipeline of task messages before being processed. Also this does not answer my question on lifetime of two objects.
As long as your function ends with p.set_value(), you are reimplementing packaged_task and you could use it instead. You also get better exception handling for free. The lifetime of the packaged_task and its future are independent and they can be destroyed in any order. The same holds true for a promise and its future.
0

shared_ptr to the rescue

void job_function(std::shared_ptr<std::promise> p) { do_it(); p->set_value(); } void foo() { std::shared_ptr<std::promise> spPromise = std::make_shared<std::promise>(); auto* thread = new std::thread(job_function, spPromise); spPromise->get_future().wait_for(std::chrono::seconds(1)); } 

Now it doesn't matter if the thread completes before or after the original function that waits returns. The promise objects gets deleted when the last instance of the shared_ptr goes away.

If you want to keep the pass by reference semantics, just keep the shared_ptr captured by value for the lifetime of the thread.

void job_function(std::promise>& p) { do_it(); p.set_value(); } void foo() { std::shared_ptr<std::promise> spPromise = std::make_shared<std::promise>(); std::promise& p = *spPromise.get(); auto* thread = new std::thread([spPromise] { job_function(*spPromise.get()); // same as job_function(p) }); p.get_future().wait_for(std::chrono::seconds(1)); } 

3 Comments

There is no need for shared_ptr when using promises. You just have to extract the future before moving the promise away. The future and its promise have a shared state that internally already uses something like shared_ptr.
@Chronial - maybe. But it's still undefined behavior for a thread to access a reference to an object that destructed in another thread. That's the issue the OP has.
The point is that one thread needs the future, the other the promise. So you create the promise, get the future from it and then move the promise to the new thread. This way the two threads have references to different objects and there's no UB. See also Homer152's answer

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.