Reputation: 2667
Note that this question has an 'answer' at here and here, but my question is not on how to get rid of the error, but why this error occurs.
Consider the following code:
include <cstdio>
#include <future>
int main() {
std::promise<int> promise;
auto future = promise.get_future();
promise.set_value(42);
auto result = future.get();
printf("%d\n", result);
}
This code throws an exception:
$ g++ -g -std=c++1z main.cpp
$ ./a.out
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error -1
Aborted (core dumped)
The solution is to pass -lpthread
on command line:
$ g++ -g -std=c++1z -lpthread main.cpp
$ ./a.out
42
Now, I am used to get a linking error when I don't link with a required library. This is the first time when I got a runtime error.
When you run the version (without lpthread
) under gdb, this is the stack trace you'll get (redacted some of it):
#5 0x00007ffff7aa6ef3 in __cxxabiv1::__cxa_throw (obj=obj@entry=0x61ad00,
tinfo=tinfo@entry=0x7ffff7dce6b0 <typeinfo for std::system_error>,
dest=dest@entry=0x7ffff7ad02b0 <std::system_error::~system_error()>)
at /tmp/tmp.kmkSDUDFn8/build/../gcc-9.1.0/libstdc++-v3/libsupc++/eh_throw.cc:95
#6 0x00007ffff7a9d0ec in std::__throw_system_error (__i=-1)
at /tmp/tmp.kmkSDUDFn8/build/x86_64-pc-linux-gnu/libstdc++-v3/include/ext/new_allocator.h:89
#7 0x000000000040240f in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__once=..., __f=
@0x7fffffffdd80: (void (std::__future_base::_State_baseV2::*)(class std::__future_base::_State_baseV2 * const, class std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>()> *, bool *)) 0x40200c <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args#0=@0x7fffffffdd78: 0x61ac30, __args#1=@0x7fffffffdd70: 0x7fffffffddf0,
__args#2=@0x7fffffffdd68: 0x7fffffffdd67) at /.../include/c++/9.1.0/mutex:697
#8 0x0000000000401e5d in std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) (this=0x61ac30, __res=..., __ignore_failure=false)
at /.../include/c++/9.1.0/future:401
So it has something to do with call_once
. Curious on why that manifests as a runtime error instead of link time.
Upvotes: 2
Views: 235
Reputation: 136208
GNU libc
provides alternative implementations of Pthread functions, so that linking succeeds even without -pthread
. See is pthread in glibc.so implemented by weak symbol to provide pthread stub functions? and Why glibc and pthread library both defined same APIs?
The GNU C++ standard std::call_once
does
int __e = __gthread_once(&__once._M_once, &__once_proxy);
__gthread_once
is:
static inline int
__gthread_once (__gthread_once_t *__once, void (*__func) (void))
{
if (__gthread_active_p ())
return __gthrw_(pthread_once) (__once, __func);
else
return -1;
}
The GNU C++ standard library detects at run-time whether pthread
library implementation is available in __gthread_active_p
function.
/* For a program to be multi-threaded the only thing that it certainly must
be using is pthread_create. However, there may be other libraries that
intercept pthread_create with their own definitions to wrap pthreads
functionality for some purpose. In those cases, pthread_create being
defined might not necessarily mean that libpthread is actually linked
in.
For the GNU C library, we can use a known internal name. This is always
available in the ABI, but no other library would define it. That is
ideal, since any public pthread function might be intercepted just as
pthread_create might be. __pthread_key_create is an "internal"
implementation symbol, but it is part of the public exported ABI. Also,
it's among the symbols that the static libpthread.a always links in
whenever pthread_create is used, so there is no danger of a false
negative result in any statically-linked, multi-threaded program.
For others, we choose pthread_cancel as a function that seems unlikely
to be redefined by an interceptor library. The bionic (Android) C
library does not provide pthread_cancel, so we do use pthread_create
there (and interceptor libraries lose). */
As the referenced answers state, one should be using -pthread
option when both compiling and linking multi-threaded programs. -lpthread
isn't sufficient because it doesn't define _REENTRANT
macro which some code may require:
$ diff <(g++ -E -dD -xc++ /dev/null) <(g++ -E -dD -xc++ -pthread /dev/null)
289a290
> #define _REENTRANT 1
Upvotes: 3