terminate called after throwing an instance of 'std::system_error'
Asked Answered
U

1

6

When i use std::call_once in Linux version 2.6.36,it makes an error:

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

Compile command:

mipsel-buildroot-linux-uclibc-g++ callonce.cpp -o callonce -static -lpthread

my code:

#include <iostream>
#include <mutex>
using namespace std;
int main()
{
    cout << "Hello world" << std::endl;
    static once_flag of;
    call_once(of,[]{});

    return 0;
}
Unwitnessed answered 17/12, 2020 at 6:24 Comment(5)
I can reproduce this, but if I replace -static -lpthread with simply -pthread then it works. Omitting -pthread, or using -static -pthread it crashes again. Try on Godbolt.Raymonraymond
I tried four combinations of -pthread, -lpthread, and -static: -pthread ...worked; -lpthread ...worked; -static -pthread ...crashed; -static -lpthread ...crashed; so,it seems a problem with -static?Unwitnessed
gcc 10.2 barfs but gcc trunc seems to handle it fine.Peon
...but this "question" is more of a statement in it's current state. For someone to be able to give an answer, you must ask a question. If the question is "is there a bug in gcc version x?", then the answer is most likely "yes".Peon
Hmm, -lpthread alone crashes on my own system, but works on godbolt.Raymonraymond
A
7

There is a major difference between linking statically and dynamically. The former only links in object files from .a which resolve currently unresolved symbols, whereas a shared library .so is linked in in its entirety regardless (unless -Wl,--as-needed linker option is used).

It so happens, that GNU C++ standard library std::call_once checks whether the application is multi-threaded by checking whether pthread_create can be resolved. Since your code doesn't invoke pthread_create or std::thread with non-default constructor, statically linking -pthread doesn't link in pthread_create, and hence std::call_once fails. The check is done by invoking __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).  */

#ifdef __GLIBC__
__gthrw2(__gthrw_(__pthread_key_create),
     __pthread_key_create,
     pthread_key_create)
# define GTHR_ACTIVE_PROXY  __gthrw_(__pthread_key_create)
#elif defined (__BIONIC__)
# define GTHR_ACTIVE_PROXY  __gthrw_(pthread_create)
#else
# define GTHR_ACTIVE_PROXY  __gthrw_(pthread_cancel)
#endif

static inline int
__gthread_active_p (void)
{
  static void *const __gthread_active_ptr
    = __extension__ (void *) &GTHR_ACTIVE_PROXY;
  return __gthread_active_ptr != 0;
}

One fix is to #include <pthread.h> and add a line or two to the top of your main function:

static_cast<void>(pthread_create);
static_cast<void>(pthread_cancel);

That causes undefined references to pthread_create and pthread_cancel and makes -static -pthread link in these functions from the static library into your application, which makes __gthread_active_p function return 1 and, in turn, enables std::call_once to work.


Another fix is to use -Wl,--undefined=pthread_create,--undefined=pthread_cancel linker command line option, which doesn't require source code changes.


Note that in the modern world using -lpthread is neither necessary nor sufficient.

Aria answered 17/12, 2020 at 21:56 Comment(2)
I get, and my demo finally worked... Thank you very much for sharing :)Unwitnessed
By the way, I had get a fix is use -Wl,--whole-archive -lrt -lpthread -Wl,--no-whole-archive, it worked on my Ubuntu-1604, but still crashed on mips32-linuxUnwitnessed

© 2022 - 2024 — McMap. All rights reserved.