I have seen terminate called after throwing an instance of 'std::system_error' - and while I believe it talks about the similar underlying problem, the proposed solution there does not work in my case.
I want to develop a cross-platform code that uses a threaded periodic timer function; here is the code:
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.14)
project(myProject LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(myProject
${CMAKE_SOURCE_DIR}/main.cpp
)
target_compile_options(myProject PRIVATE -Wall -Wextra)
#target_link_libraries(myProject PUBLIC -static) # this causes "terminate called after throwing an instance of 'std::system_error'; what(): Unknown error 1992352464"
target_link_libraries(myProject PUBLIC
stdc++fs
)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(myProject PUBLIC Threads::Threads)
main.cpp
#include <cinttypes>
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <fstream>
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#define CHOSEN_CLOCK std::chrono::high_resolution_clock
// class Timer below derived from:
// .h
// /
//
class Timer {
std::atomic<bool> active{true};
public:
std::chrono::time_point<CHOSEN_CLOCK> timer_entry_ts; // SO:31497531
template<typename Function>
void setInterval(Function function, int interval);
void stop();
};
template<typename Function>
void Timer::setInterval(Function function, int interval) {
active = true;
std::thread t([=]() {
while(active.load()) {
timer_entry_ts = CHOSEN_CLOCK::now();
function();
if(!active.load()) return;
std::this_thread::sleep_until(timer_entry_ts + std::chrono::milliseconds(interval));
}
});
t.detach(); // it seems this causes "terminate called after throwing an instance of 'std::system_error' ; what(): Unknown error 1992278736"; if we do a -static linkage in build on Linux
}
void Timer::stop() {
active = false;
}
Timer mtimer;
std::chrono::time_point<CHOSEN_CLOCK> last_entry_ts{};
void timer_function(void)
{
long long int delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(mtimer.timer_entry_ts-last_entry_ts).count();
printf("%7.3f ms\n", delta_micros/1000.0f);
last_entry_ts = mtimer.timer_entry_ts;
}
int main(const int argc, char** argv)
{
(void)argc; // warning: unused parameter
(void)argv; // warning: unused parameter
static_cast<void>(pthread_create);
static_cast<void>(pthread_cancel);
#ifdef _GLIBCXX_HAS_GTHREADS
printf("_GLIBCXX_HAS_GTHREADS is defined.\n");
#else
printf("_GLIBCXX_HAS_GTHREADS is NOT defined!\n");
#endif
last_entry_ts = CHOSEN_CLOCK::now();
mtimer.setInterval(timer_function, 25); // repeat timer_function at 25 ms period
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // wait for 2 seconds
mtimer.stop();
printf("Thread stopped\n");
std::exit(EXIT_SUCCESS);
}
I build this code using:
mkdir build && cd build
cmake ../ -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles"
make
... and I build it both in
- MINGW64 (under Windows 10), where I have
g++.exe (Rev3, Built by MSYS2 project) 14.2.0
- Raspbian Stretch on Raspberry Pi 3B+, where I have
g++ (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516
If I build the code as posted, then I can see the threaded timer_function
run on both platforms.
However, if the line target_link_libraries(myProject PUBLIC -static)
is uncommented (enabled) in CMakeLists.txt
and the project rebuilt - then it again works in MINGW64/Windows, howevers it fails on Raspbian with:
$ ./myProject
_GLIBCXX_HAS_GTHREADS is defined.
0.202 ms
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error 1995920080
Aborted
Now, it is quite unfortunate that I get "Unknown error 1995920080" so I cannot look in further into it; however, the "0.202 ms" printout tells me timer_function
has ran once. Best I could do was to confirm via objdump -S ./myProject | less
that t.detach();
seems to call _ZNSt6thread6detachEv
- and using gdb --args ./myProject
I can set a breakpoint on that function, however gdb
does not let me step through it, not even in TUI mode (it says "[ No Source Available ]"); all I can say, is that once I press s
tep in gdb
after hitting that breakpoint, I get the above error:
(gdb) b _ZNSt6thread6detachEv
Breakpoint 1 at 0x612b0
(gdb) r
...
_GLIBCXX_HAS_GTHREADS is defined.
[New Thread 0x76ffc2d0 (LWP 32461)]
1.260 ms
Thread 1 "myProject" hit Breakpoint 1, 0x000612b0 in std::thread::detach() ()
(gdb) s
Single stepping until exit from function _ZNSt6thread6detachEv,
which has no line number information.
24859.580 ms
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error 1996473040
Thread 1 "myProject" received signal SIGABRT, Aborted.
raise (sig=<optimized out>) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 raise (sig=<optimized out>) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x000b6eb8 in abort ()
#2 0x00000020 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(Note: I saw here /include/bits/std_thread.h#L324 that detach()
might end up as inline void thread::detach() { std::__throw_system_error(EINVAL); }
if _GLIBCXX_HAS_GTHREADS
is not defined, but apparently it is, both in my MINGW64/Windows and Raspbian environments)
Any ideas how can I get the -static
build of this example to work also on Raspbian Stretch?
I have seen terminate called after throwing an instance of 'std::system_error' - and while I believe it talks about the similar underlying problem, the proposed solution there does not work in my case.
I want to develop a cross-platform code that uses a threaded periodic timer function; here is the code:
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.14)
project(myProject LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(myProject
${CMAKE_SOURCE_DIR}/main.cpp
)
target_compile_options(myProject PRIVATE -Wall -Wextra)
#target_link_libraries(myProject PUBLIC -static) # this causes "terminate called after throwing an instance of 'std::system_error'; what(): Unknown error 1992352464"
target_link_libraries(myProject PUBLIC
stdc++fs
)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(myProject PUBLIC Threads::Threads)
main.cpp
#include <cinttypes>
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <fstream>
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#define CHOSEN_CLOCK std::chrono::high_resolution_clock
// class Timer below derived from:
// https://raw.githubusercontent/99x/timercpp/refs/heads/master/timercpp.h
// https://www.fluentcpp/2018/12/28/timer-cpp/
// https://stackoverflow/q/21057676
class Timer {
std::atomic<bool> active{true};
public:
std::chrono::time_point<CHOSEN_CLOCK> timer_entry_ts; // SO:31497531
template<typename Function>
void setInterval(Function function, int interval);
void stop();
};
template<typename Function>
void Timer::setInterval(Function function, int interval) {
active = true;
std::thread t([=]() {
while(active.load()) {
timer_entry_ts = CHOSEN_CLOCK::now();
function();
if(!active.load()) return;
std::this_thread::sleep_until(timer_entry_ts + std::chrono::milliseconds(interval));
}
});
t.detach(); // it seems this causes "terminate called after throwing an instance of 'std::system_error' ; what(): Unknown error 1992278736"; if we do a -static linkage in build on Linux
}
void Timer::stop() {
active = false;
}
Timer mtimer;
std::chrono::time_point<CHOSEN_CLOCK> last_entry_ts{};
void timer_function(void)
{
long long int delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(mtimer.timer_entry_ts-last_entry_ts).count();
printf("%7.3f ms\n", delta_micros/1000.0f);
last_entry_ts = mtimer.timer_entry_ts;
}
int main(const int argc, char** argv)
{
(void)argc; // warning: unused parameter
(void)argv; // warning: unused parameter
static_cast<void>(pthread_create);
static_cast<void>(pthread_cancel);
#ifdef _GLIBCXX_HAS_GTHREADS
printf("_GLIBCXX_HAS_GTHREADS is defined.\n");
#else
printf("_GLIBCXX_HAS_GTHREADS is NOT defined!\n");
#endif
last_entry_ts = CHOSEN_CLOCK::now();
mtimer.setInterval(timer_function, 25); // repeat timer_function at 25 ms period
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // wait for 2 seconds
mtimer.stop();
printf("Thread stopped\n");
std::exit(EXIT_SUCCESS);
}
I build this code using:
mkdir build && cd build
cmake ../ -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles"
make
... and I build it both in
- MINGW64 (under Windows 10), where I have
g++.exe (Rev3, Built by MSYS2 project) 14.2.0
- Raspbian Stretch on Raspberry Pi 3B+, where I have
g++ (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516
If I build the code as posted, then I can see the threaded timer_function
run on both platforms.
However, if the line target_link_libraries(myProject PUBLIC -static)
is uncommented (enabled) in CMakeLists.txt
and the project rebuilt - then it again works in MINGW64/Windows, howevers it fails on Raspbian with:
$ ./myProject
_GLIBCXX_HAS_GTHREADS is defined.
0.202 ms
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error 1995920080
Aborted
Now, it is quite unfortunate that I get "Unknown error 1995920080" so I cannot look in further into it; however, the "0.202 ms" printout tells me timer_function
has ran once. Best I could do was to confirm via objdump -S ./myProject | less
that t.detach();
seems to call _ZNSt6thread6detachEv
- and using gdb --args ./myProject
I can set a breakpoint on that function, however gdb
does not let me step through it, not even in TUI mode (it says "[ No Source Available ]"); all I can say, is that once I press s
tep in gdb
after hitting that breakpoint, I get the above error:
(gdb) b _ZNSt6thread6detachEv
Breakpoint 1 at 0x612b0
(gdb) r
...
_GLIBCXX_HAS_GTHREADS is defined.
[New Thread 0x76ffc2d0 (LWP 32461)]
1.260 ms
Thread 1 "myProject" hit Breakpoint 1, 0x000612b0 in std::thread::detach() ()
(gdb) s
Single stepping until exit from function _ZNSt6thread6detachEv,
which has no line number information.
24859.580 ms
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error 1996473040
Thread 1 "myProject" received signal SIGABRT, Aborted.
raise (sig=<optimized out>) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 raise (sig=<optimized out>) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x000b6eb8 in abort ()
#2 0x00000020 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(Note: I saw here https://github/gcc-mirror/gcc/blob/73987e69/libstdc%2B%2B-v3/include/bits/std_thread.h#L324 that detach()
might end up as inline void thread::detach() { std::__throw_system_error(EINVAL); }
if _GLIBCXX_HAS_GTHREADS
is not defined, but apparently it is, both in my MINGW64/Windows and Raspbian environments)
Any ideas how can I get the -static
build of this example to work also on Raspbian Stretch?
2 Answers
Reset to default 1Why this seems to happen when -static
is used is really the wrong question.
The problem is that you reach the end of main
and exit your program without ensuring that the background thread you created has actually been stopped.
You can't just exit from main
while other threads are running; signaling the thread to stop is not enough, so you need to either change your stop
code to wait for the thread to stop, or to provide another mechanism (e.g. by returning the std::thread
object and calling join
.
Ok, this maybe does not answer the question as posed - but at least, it will provide a bit more understanding into where does the error occur; and it does indeed seem to occur in thread::detach
.
So, since on Raspbian Stretch I have g++ 6.3.0, I looked up thread::detach
for that version (unfortunately have to git clone --depth 1 --branch releases/gcc-6.3.0 https://github/gcc-mirror/gcc.git
[ref], can't search code directly in GitHub in a given tag) and found it in https://github/gcc-mirror/gcc/blob/releases/gcc-6.3.0/libstdc%2B%2B-v3/src/c%2B%2B11/thread :
void
thread::detach()
{
int __e = EINVAL;
if (_M_id != id())
__e = __gthread_detach(_M_id._M_thread);
if (__e)
__throw_system_error(__e);
_M_id = id();
}
So, indeed, thread::detach
itself can throw a system error, if the return from __gthread_detach
is anything but zero. And __gthread_detach
merely calls pthread_detach
, whose docs say:
RETURN VALUE
On success, pthread_detach() returns 0; on error, it returns an
error number.
ERRORS
EINVAL thread is not a joinable thread.
ESRCH No thread with the ID thread could be found.
... although, above I get neither 3 (ESRCH, No such process) nor 22 (EINVAL, Invalid argument), as numeric error value.
I tried a couple of more things in order to get better debug overview, which ultimately failed to give me more debug information:
- Did
sudo apt install libstdc++6-6-dbg
to get libstc++ debug symbols on Raspbian - had to manually look into http://legacy.raspbian./raspbian/pool/main/g/gcc-6/ to see what package would correspond - Used
target_compile_definitions(myProject PUBLIC -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_BACKTRACE)
in CMakeLists.txt, so as to use the libstdc++ debug mode but that did not quite work either (especially no backtraces because I could not findlibstdc++exp
orlibstdc++_libbacktrace
to link against on this platform) - Tried to link with
target_link_libraries(myProject PUBLIC /usr/lib/arm-linux-gnueabihf/debug/libstdc++.a -static-libgcc -static)
- but since I did not have the source files, did not get much info from this ingdb
So, had to go to inspect direct assembly in gdb
, and even if I do not know assembly, it at least revealed something. Couple of caveats:
- If
gdb
complains of "[ No Source Available ]", then in gdb TUI (Ctrl-x Ctrl-a) mode, enterlayout asm
to make the upper window display assembly ... usestepi
ornexti
(which can be abbreviated tosi
orni
) to step through your machine code - On my Raspbian, the window display gets messed up after every
stepi
command - to get around this, can use therefresh
approach - and we can even directly redefinesi
as:
define si
stepi
refresh
end
So, here is how the gdb
session looks like:
$ gdb --args ./myProject
GNU gdb (Raspbian 7.12-6) 7.12.0.20161007-git
...
(gdb) define si
Type commands for definition of "si".
End with a line saying just "end".
>stepi
>refresh
>end
(gdb) b __gthread_detach
Breakpoint 1 at 0x4771c
(gdb) r
Starting program: ~/code/test/build/myProject
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
_GLIBCXX_HAS_GTHREADS is defined.
[New Thread 0x76ffc2d0 (LWP 1367)]
1.676 ms
Thread 1 "myProject" hit Breakpoint 1, 0x0004771c in __gthread_detach(unsigned long) ()
(gdb) bt
#0 0x0004771c in __gthread_detach(unsigned long) ()
#1 0x76ffc2d0 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) C-x C-a
(gdb) layout asm
At this point, the gdb
screen (after some scrolling) looks like this:
│0x47710 <_ZL16__gthread_detachm+24> mov r3, r0 │
│0x47714 <_ZL16__gthread_detachm+28> mov r0, r3 │
│0x47718 <_ZL16__gthread_detachm+32> sub sp, r11, #4 │
B+>│0x4771c <_ZL16__gthread_detachm+36> pop {r11, pc} │
│0x47720 <_ZN9__gnu_cxxL18__exchange_and_addEPVii> push {r11} ; (str r11, [sp, #-4]!) │
│0x47724 <_ZN9__gnu_cxxL18__exchange_and_addEPVii+4> add r11, sp, #0 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
multi-thre Thread 0x18c000 (LW In: __gthread_detach L?? PC: 0x4771c
(gdb)
So, it seems gdb
managed to break at the very exit of __gthread_detach
... So, the next thing is si
:
│0x47e24 <_ZNSt6thread6detachEv+76> bl 0x476f8 <_ZL16__gthread_detachm> │
>│0x47e28 <_ZNSt6thread6detachEv+80> str r0, [r11, #-8] │
│0x47e2c <_ZNSt6thread6detachEv+84> ldr r3, [r11, #-8] │
│0x47e30 <_ZNSt6thread6detachEv+88> cmp r3, #0 │
│0x47e34 <_ZNSt6thread6detachEv+92> beq 0x47e40 <_ZNSt6thread6detachEv+104> │
│0x47e38 <_ZNSt6thread6detachEv+96> ldr r0, [r11, #-8] │
│0x47e3c <_ZNSt6thread6detachEv+100> bl 0x8959c <_ZSt20__throw_system_errori> │
│0x47e40 <_ZNSt6thread6detachEv+104> sub r3, r11, #12 │
│0x47e44 <_ZNSt6thread6detachEv+108> mov r0, r3 │
│0x47e48 <_ZNSt6thread6detachEv+112> bl 0x10aa0 <std::thread::id::id()> │
│0x47e4c <_ZNSt6thread6detachEv+116> ldr r3, [r11, #-24] ; 0xffffffe8 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
multi-thre Thread 0x18c000 (LW In: std::thread::detach L?? PC: 0x47e34
(gdb) si
0x00047e28 in std::thread::detach() ()
(gdb) si
0x00047e2c in std::thread::detach() ()
(gdb) si
0x00047e30 in std::thread::detach() ()
(gdb) si
0x00047e34 in std::thread::detach() ()
(gdb) info registers r0
r0 0x76ffc2d0 1996473040
(gdb) info registers r3
r3 0x76ffc2d0 1996473040
(gdb) si
0x00047e38 in std::thread::detach() ()
(gdb) si
0x00047e3c in std::thread::detach() ()
(gdb) si
0x0008959c in std::__throw_system_error(int) ()
SO, in the above snippet, we start right at the next instruction after the bl 0x476f8 <_ZL16__gthread_detachm>
. The next few instructions follow in sequence, and if we assume that also here return values that fit into a register are simply stored into the first register (r0), then the return from __gthread_detach
is in r0
, which has the non-zero value 1996473040; that value seems to get copied to register r3
for purposes of comparing to zero, and since that comparison fails, we never branch via beq 0x47e40 <_ZNSt6thread6detachEv+104>
, and one instruction later, the system error is thrown via bl 0x8959c <_ZSt20__throw_system_errori>
. And continuing at this point will generate:
(gdb) c
Continuing.
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error 1996473040
Note that the numeric value for the "Unknown error" is exactly the value of the r0
register inspected earlier.
So the problem is, when linking with -static
in this case, that __gthread_detach
- that is, pthread_detach
- when called from thread::detach
returns a non-zero value, indicating an error; and therefore, thread::detach
itself throws a system error via __throw_system_error
.
Now, why would -static have influence on this, is unclear to me ...
(void)argc; // warning: unused parameter
, when you just writeint main()
, and then on the next lines you use astatic_cast<void>
which is basically doing the same, just nicer. Inconsistency is the enemy of claritry. I would even prefer all(void)
, but better of course usestatic_cast<void>
consistently – 463035818_is_not_an_ai Commented Mar 19 at 13:45[[maybe_unused]]
on the unused parameters. (But I agree with ^ and would just omit them altogether viaint main()
.) – Eljay Commented Mar 19 at 13:49-static
on linux ? it is needed on windows because windows uses msvcrt, andpthread
on windows is just a thin wrapper around win32 API, but linux should ship gcc libraries and pthreads that depend on the system libc, you shouldn't static linkpthreads
on linux, worst case is you are using a newer gcc than the one on system then you just-static-libstdc++ -static-libgcc
– Ahmed AEK Commented Mar 19 at 14:01-static
on linux because 1) I just want to have as few conditionals as possible in the CMakeLists.txt and 2) should I have to use the executable on a different (32-bit) Raspbian, I'd have wanted to avoid possible incompatibilities as much as possible. Btw, would-static-libstdc++ -static-libgcc
also link in/handlepthreads
? – sdbbs Commented Mar 19 at 14:42