Причина неизвестного исключения в обещании без lpthread - PullRequest
2 голосов
/ 24 февраля 2020

Обратите внимание, что этот вопрос имеет ответ на здесь и здесь , но мой вопрос не в том, как избавиться от ошибки, а в том, почему эта ошибка возникает.

Рассмотрим следующий код:

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);
}

Этот код вызывает исключение:

$ 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)

Решение состоит в том, чтобы передать -lpthread в командной строке:

$ g++ -g -std=c++1z -lpthread main.cpp
$ ./a.out
42

Теперь я привык получать ошибку компоновки, когда не связываюсь с необходимой библиотекой. Это первый раз, когда я получил ошибку во время выполнения.

Когда вы запускаете версию (без lpthread) в gdb, вы получите трассировку стека (отредактированную часть из нее):

#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

Так что это как-то связано с call_once. Любопытно, почему это проявляется как ошибка времени выполнения вместо времени ссылки.

1 Ответ

2 голосов
/ 24 февраля 2020

GNU libc предоставляет альтернативные реализации функций Pthread, так что связывание успешно даже без -pthread. Смотрите, - это pthread в glib c .so, реализованный слабым символом для обеспечения функций-заглушек pthread? и Почему glib c и библиотека pthread оба определяют одни и те же API?

Стандарт GNU C ++ std::call_once делает

int __e = __gthread_once(&__once._M_once, &__once_proxy);

__gthread_once:

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;
}

Стандартная библиотека GNU C ++ во время выполнения определяет, доступна ли реализация библиотеки pthread в функции __gthread_active_p.

/* 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).  */

Поскольку указанные ответы указывают на необходимость использования опции -pthread при компиляции и компоновке многопоточных программ. -lpthread недостаточно, поскольку он не определяет макрос _REENTRANT, который может потребоваться для некоторого кода:

$ diff <(g++ -E -dD -xc++ /dev/null) <(g++ -E -dD -xc++ -pthread /dev/null)
289a290
> #define _REENTRANT 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...