6

When I run the following code,

#include <future>

int main()
{
    std::promise<int> p;
    p.set_value(1);
    return 0;
}

std::system_error is thrown. However, when I set the promise's value in another thread,

#include <future>
#include <thread>

int main()
{
    std::promise<int> p;
    std::thread([&]{p.set_value(1);}).join();
    return 0;
}

everything works fine. From what I understand about std::promise, calling set_value shouldn't throw an exception unless the promise has no shared state (i.e. it's been moved from) or a value has already been assigned to it, and even then it would throw std::future_error, not std::system_error. Since there's no data race or anything of that sort, it shouldn't matter whether I call set_value from the thread in which I created the promise or in another thread. Is there something I'm missing?

I've tried this using both g++ and clang with the same results. Specifically, when I run the code at the top, the following is output to stderr:

terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
Aborted (core dumped)

These commands were used to compile the code at the top,

g++ main_thread.cpp -o main_thread -std=c++17 -g -Og
clang main_thread.cpp -o main_thread -std=c++17 -lstdc++

and these were used to compile the code at the bottom:

g++ separate_thread.cpp -o separate_thread -lpthread -std=c++17 -g -Og
clang separate_thread.cpp -o separate_thread -std=c++17 -lstdc++ -lpthread
Isaac Saffold
  • 1,116
  • 1
  • 11
  • 20
  • Very related/maybe dupe: https://stackoverflow.com/questions/66141204/crash-in-stdpromiseset-value-crash. Can't explain why no exception is thrown for the second case. – NathanOliver Mar 16 '21 at 14:20
  • 2
    @MarekR consider [this verbatim reproduction](https://www.godbolt.org/z/1Th44r). – Drew Dormann Mar 16 '21 at 14:28
  • 1
    If you add `-stdlib=libc++` to the clang pane on godbolt, you get no error. – Marshall Clow Mar 16 '21 at 14:32
  • 1
    @NathanOliver This isn't a dupe. That poster tried to set the value of a promise that had been moved from, and that therefore had no shared state. In my case, the promise had a shared state. – Isaac Saffold Mar 16 '21 at 14:41
  • 2
    @IsaacSaffold A default constructed promise also has no shared state: https://en.cppreference.com/w/cpp/thread/promise/promise – NathanOliver Mar 16 '21 at 14:45
  • 3
    Oh, wait. it's empty, not has none. Not sure anymore. – NathanOliver Mar 16 '21 at 14:46
  • sounds fishy. I think it should be just fine. Can you trace into it using the debugger, perhaps breaking when an exception is thrown? I wonder if you need some compiler options to link it properly, while using `` also happens to automatically bring it in. – JDługosz Mar 16 '21 at 14:53
  • 1
    It works with both gcc and clang if I compile with either `-fsanitize=address` or `-pthread` and I don't know why. – Kevin Mar 16 '21 at 14:55
  • @JDługosz I traced into it with GDB for the g++ version and the exception was indeed thrown within the call to `std::promise::set_value`. – Isaac Saffold Mar 16 '21 at 15:04
  • I'll post an edit with the compiler options I used. I think that might help. – Isaac Saffold Mar 16 '21 at 15:05
  • It might indeed require the threading library, since it needs to contain locks. "within the call" isn't useful; what was it actually trying to do that caused the error? Like, maybe acquire a mutex? Note Kevin's comment above. – JDługosz Mar 16 '21 at 15:11
  • Have you seen https://stackoverflow.com/questions/15778085/is-stdpromise-broken-on-my-machine-using-g-mp/15794105#15794105 ? It could be dynamic linker paths or otherwise using an old version of the standard library. – JDługosz Mar 16 '21 at 15:15
  • @DrewDormann The trunk versions of both compilers run well, strange. The issue itself looks really like trying to relock an already locked mutex or something similar. – Secundi Mar 16 '21 at 15:17
  • 3
    Oh. When I use `-pthread` instead of `-lpthread`, it works. I looked at the man page for `g++` and apparently the former is the correct way to link it. And @JDługosz , the backtrace is unintelligible. They're all function templates with several arguments and names spanning several lines each. – Isaac Saffold Mar 16 '21 at 15:19
  • 2
    Related [Difference between -pthread and -lpthread while compiling](https://stackoverflow.com/questions/23250863/difference-between-pthread-and-lpthread-while-compiling) – 273K Mar 16 '21 at 15:26

1 Answers1

4

std::promise is part of the Thread Support Library so it would stand to reason that it requires enabling thread support in your compiler options (e.g. -pthread).

Note that -pthread affects the compilation as well as linkage stages. From man g++:

-pthread
Adds support for multithreading with the pthreads library. This option sets flags for both the preprocessor and linker.

Woodford
  • 3,746
  • 1
  • 15
  • 29