I am using a C++ logging library on a FreeBSD 10 machine and I am running into trouble closing threads when receiving a sigint.
A created a GitHub project for testing purposes (link). If you build it on FreeBSD 10, execute it and press [ctrl+c] it will terminate. You can find the build commands I use below.
$ git clone git#github.com:tijme/free-bsd-thread-bug.git
$ cd free-bsd-thread-bug && mkdir -p cmake-build-debug && cd cmake-build-debug
$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER="/usr/local/bin/gcc6" -DCMAKE_CXX_COMPILER="/usr/local/bin/g++6"
$ make -dA
$ ./FreeBSDThreadBug
Code I used (can also be found in the GitHub repository)
/* main.cpp */
#include "Example.h"
#include <iostream>
#include <csignal>
#include <thread>
#include <chrono>
Example* example = new Example();
void onSignal(int signum)
{
delete example;
exit(0);
}
int main() {
signal(SIGINT, onSignal);
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
return 0;
}
/* Example.h */
#ifndef FREEBSDTHREADBUG_EXAMPLE_H
#define FREEBSDTHREADBUG_EXAMPLE_H
#include <thread>
#include <iostream>
#include <chrono>
class Example {
public:
Example();
~Example();
std::thread threadHandle;
void threadFunction();
};
#endif //FREEBSDTHREADBUG_EXAMPLE_H
/* Example.cpp */
#include "Example.h"
#include <thread>
#include <chrono>
Example::Example()
{
std::cout << "Main: starting thread" << std::endl;
threadHandle = std::thread(&Example::threadFunction, this);
std::cout << "Main: thread started" << std::endl;
}
Example::~Example()
{
std::cout << "THIS ID: " << std::this_thread::get_id() << std::endl;
std::cout << "THREAD ID: " << threadHandle.get_id() << std::endl;
std::cout << "Main: joining thread" << std::endl;
threadHandle.join();
std::cout << "Main: thread joined" << std::endl;
}
void Example::threadFunction() {
std::cout << "Thread: starting to sleep" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
std::cout << "Thread: sleep finished" << std::endl;
}
Correct output (on e.g. MacOS Sierra):
As you can see the ID's of the threads are different, as expected.
$ ./FreeBSDThreadBug
Main: starting thread
Main: thread started
Thread: starting to sleep
^C
THIS ID: 0x7fffa428a3c0
THREAD ID: 0x70000c044000
Main: joining thread
Thread: sleep finished
Main: thread joined
Wrong output (termination, on FreeBSD 10.3):
The thread ID's are the same here, which is pretty weird.
$ ./FreeBSDThreadBug
Main: starting thread
Main: thread started
Thread: starting to sleep
^C
THIS ID: 0x801c06800
THREAD ID: 0x801c06800
Main: joining thread
terminate called after throwing an instance of 'std::system_error'
what(): Resource deadlock avoided
Abort (core dumped)
Core dump
Core was generated by `FreeBSDThreadBug'.
Program terminated with signal SIGABRT, Aborted.
#0 0x00000008012d335a in thr_kill () from /lib/libc.so.7
[Current thread is 1 (LWP 100146)]
(gdb) bt full
#0 0x00000008012d335a in thr_kill () from /lib/libc.so.7
No symbol table info available.
#1 0x00000008012d3346 in raise () from /lib/libc.so.7
No symbol table info available.
#2 0x00000008012d32c9 in abort () from /lib/libc.so.7
No symbol table info available.
#3 0x0000000800ad8afd in __gnu_cxx::__verbose_terminate_handler () at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/vterminate.cc:95
terminating = true
t = <optimized out>
#4 0x0000000800ad5b48 in __cxxabiv1::__terminate (handler=<optimized out>) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:47
No locals.
#5 0x0000000800ad5bb1 in std::terminate () at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:57
No locals.
#6 0x0000000800ad5dc8 in __cxxabiv1::__cxa_throw (obj=obj#entry=0x80200e0a0, tinfo=0x800dd0bc0 <typeinfo for std::system_error>, dest=0x800b073b0 <std::system_error::~system_error()>)
at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_throw.cc:87
globals = <optimized out>
#7 0x0000000800b04cd1 in std::__throw_system_error (__i=11) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/functexcept.cc:130
No locals.
#8 0x0000000800b0792c in std::thread::join (this=0x801c5c058) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/thread.cc:139
__e = <optimized out>
#9 0x00000000004016fc in Example::~Example (this=0x801c5c058, __in_chrg=<optimized out>) at /root/FreeBSDThreadBug/Example.cpp:18
No locals.
#10 0x00000000004010b7 in onSignal (signum=2) at /root/FreeBSDThreadBug/main.cpp:11
No locals.
#11 0x000000080082fb4a in ?? () from /lib/libthr.so.3
No symbol table info available.
#12 0x000000080082f22c in ?? () from /lib/libthr.so.3
No symbol table info available.
#13 <signal handler called>
No symbol table info available.
#14 0x00000008012efb5a in _nanosleep () from /lib/libc.so.7
No symbol table info available.
#15 0x000000080082cc4c in ?? () from /lib/libthr.so.3
No symbol table info available.
#16 0x000000000040155d in std::this_thread::sleep_for<long, std::ratio<1l, 1000l> > (__rtime=...) at /usr/local/lib/gcc6/include/c++/thread:322
__s = {__r = 2}
__ns = {__r = 500000000}
__ts = {tv_sec = 1, tv_nsec = 126917539}
#17 0x000000000040177a in Example::threadFunction (this=0x801c5c058) at /root/FreeBSDThreadBug/Example.cpp:24
No locals.
#18 0x0000000000402432 in std::__invoke_impl<void, void (Example::* const&)(), Example*>(std::__invoke_memfun_deref, void (Example::* const&)(), Example*&&) (
__f=#0x801c5e050: (void (Example::*)(Example * const)) 0x40172c <Example::threadFunction()>, __t=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>)
at /usr/local/lib/gcc6/include/c++/functional:227
No locals.
#19 0x00000000004023bf in std::__invoke<void (Example::* const&)(), Example*>(void (Example::* const&)(), Example*&&) (__fn=#0x801c5e050: (void (Example::*)(Example * const)) 0x40172c <Example::threadFunction()>,
__args#0=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>) at /usr/local/lib/gcc6/include/c++/functional:251
No locals.
#20 0x0000000000402370 in std::_Mem_fn_base<void (Example::*)(), true>::operator()<Example*>(Example*&&) const (this=0x801c5e050,
__args#0=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>) at /usr/local/lib/gcc6/include/c++/functional:604
No locals.
#21 0x000000000040233b in std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0x801c5e048) at /usr/local/lib/gcc6/include/c++/functional:1391
No locals.
#22 0x0000000000402289 in std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)>::operator()() (this=0x801c5e048) at /usr/local/lib/gcc6/include/c++/functional:1380
No locals.
#23 0x0000000000402268 in std::thread::_State_impl<std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)> >::_M_run() (this=0x801c5e040) at /usr/local/lib/gcc6/include/c++/thread:196
No locals.
#24 0x0000000800b0769f in std::execute_native_thread_routine (__p=0x801c5e040) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/thread.cc:83
__t = std::unique_ptr<std::thread::_State> containing 0x801c5e040
#25 0x000000080082a855 in ?? () from /lib/libthr.so.3
No symbol table info available.
#26 0x0000000000000000 in ?? ()
No symbol table info available.
Backtrace stopped: Cannot access memory at address 0x7fffdfffe000
System information
$ freebsd-version
10.3-RELEASE
$ /usr/local/bin/gcc6 --version
gcc6 (FreeBSD Ports Collection) 6.3.0
$ /usr/local/bin/g++6 --version
g++6 (FreeBSD Ports Collection) 6.3.0
$ cmake --version
cmake version 3.7.2
The original issue I created can be found on GitHub (link), however there is no fix yet.
I hope someone will be able to help me fix this issue. Thanks in advance.
That's not a bug, that's a feature.
You don't get a guarantee about where your signal is going to be delivered, and the set of things you're allowed to do in a signal handler is restricted.
See sigaction(3) for details about what you can do (and you can't do anything else). Your program is doing many things that are not allowed in a signal handler.
The correct thing to do is to signal something else in your program and return from the signal handler. An example technique for doing that is the "self pipe trick". Create a pipe and keep a handle to both ends. Read from one end in your normal I/O processing. If you get a signal, in the signal handler, write a byte to the other end of the pipe and return. When the byte is read from the pipe you know the signal has arrived and you can do extended processing safely.
Update:
As Michael Burr has pointed out, you can block particular threads from receiving particular signals using pthread_sigmask(3). However, to fix the underlying problem here you still need to not do the work in the signal handler.
Related
This question has been asked before but the solutions proposed there do not work for me. The issue is when an unhandled exception occurs and std::terminate is invoked the original stack trace where the exception is thrown is gone. How can we get that?
Here is a simple test app where child thread throw an exception and std:: terminated is called. The core file does not show the original stack trace.
I tried below, none of them works --
i) std::set_terminate(myhandler) -- myhandler is called as expected but this does not affect stack unwinding.
ii) compile with -fno-exceptions -- no effect, likely because the exception is thrown from std library.
Any suggestions will be greatly appreciated!
$ cat thread.cpp
#include <utility>
#include <thread>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <vector>
void f1()
{
for (int i = 0; i < 10; ++i) {
std::cout << "Thread 1 executing\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// !!! create an exception, how to get the stack trace here.
std::vector<int> vec;
vec.reserve(-1);
}
}
int main()
{
std::thread t1(f1);
t1.join();
std::cout << "Done " << '\n';
}
$ g++ -g thread.cpp -pthread
$ ./a.out
Thread 1 executing
terminate called after throwing an instance of 'std::length_error'
what(): vector::reserve
Aborted (core dumped)
$gdb a.out core
...
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
#0 0x00007f7e13084277 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 return INLINE_SYSCALL (tgkill, 3, pid, selftid, sig);
[Current thread is 1 (Thread 0x7f7e1304d700 (LWP 106036))]
Missing separate debuginfos, use: debuginfo-install libgcc-4.8.5-28.el7_5.1.x86_64 libstdc++-4.8.5-28.el7_5.1.x86_64
(gdb) thread apply all bt
Thread 2 (Thread 0x7f7e14054740 (LWP 106035)):
#0 0x00007f7e13423f97 in pthread_join (threadid=140179461691136, thread_return=0x0) at pthread_join.c:92
#1 0x00007f7e13c03e37 in std::thread::join() () from /lib64/libstdc++.so.6
#2 0x0000000000400f8f in main () at thread.cpp:22
Thread 1 (Thread 0x7f7e1304d700 (LWP 106036)):
#0 0x00007f7e13084277 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1 0x00007f7e13085968 in __GI_abort () at abort.c:90
#2 0x00007f7e13baf7d5 in __gnu_cxx::__verbose_terminate_handler() () from /lib64/libstdc++.so.6
#3 0x00007f7e13bad746 in ?? () from /lib64/libstdc++.so.6
#4 0x00007f7e13bad773 in std::terminate() () from /lib64/libstdc++.so.6
#5 0x00000000004023de in execute_native_thread_routine ()
#6 0x00007f7e13422e25 in start_thread (arg=0x7f7e1304d700) at pthread_create.c:308
#7 0x00007f7e1314cbad in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
I'm building a simple utility program that queries a mysql database, and uses regex to isolate strings in the table data.
I'm using MariaDB c++/connector, and the latest versions of MariaDB. The code was copied from the MariaDB website. I have simplified the software to illustrate the problem. See below:
// g++ -o mariadb_connect mariadb_connect.cpp -lmariadbcpp
// From https://mariadb.com/docs/clients/connector-cpp/
// with three additional lines that cause segfault
#include <iostream>
#include <mariadb/conncpp.hpp>
#include <regex> // <-- Added to the example
int main()
{
try
{
// Instantiate Driver
sql::Driver* driver = sql::mariadb::get_driver_instance();
// Configure Connection
// The URL or TCP connection string format is
// ``jdbc:mariadb://host:port/database``.
sql::SQLString url("jdbc:mariadb://localhost:3306/??????");
// Use a properties map for the user name and password
sql::Properties properties({
{"user", "???????"},
{"password", "????????"}
});
// Establish Connection
// Use a smart pointer for extra safety
std::unique_ptr<sql::Connection> conn(driver->connect(url, properties));
// Use Connection
std::cout << "Using the connection" << std::endl; // <-- Added
std::regex regexp("(faststatic.com)(.*)"); // <-- Added (Causes segfault)
// Close Connection
conn->close();
}
// Catch Exceptions
catch (sql::SQLException& e)
{
std::cout << "Error Connecting to MariaDB Platform: "
<< e.what() << std::endl;
// Exit (Failed)
return 1;
}
// Exit (Success)
return 0;
}
(???? used for private data)
Compiled with g++ on an AWS EC2 instance running Amazon Linux 2 AMI.
Compiles fine and runs fine until I added the std::regex regexp(...)
line. It still compiles fine with the addition, but on execution calls
a segfault.
I have used gdb which provides the following output with breakpoint set
to main.
(gdb) b main
Breakpoint 1 at 0x40404b: file mariadb_connect.cpp, line 15.
(gdb) run
Starting program: /home/msellers/proj/preload_images/spike/mariadb_connect
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x000000000064a588 in ?? ()
Here is the output of the gdb bt command after the segfault:
(gdb) bt
#0 0x000000000064a588 in ?? ()
#1 0x0000000000409155 in std::__detail::_Scanner<char>::_M_scan_normal (this=0x7fffffffe018) at /usr/include/c++/7/bits/regex_scanner.tcc:119
#2 0x00000000004084a1 in std::__detail::_Scanner<char>::_M_advance (this=0x7fffffffe018) at /usr/include/c++/7/bits/regex_scanner.tcc:80
#3 0x00007ffff7c3e060 in std::__detail::_Compiler<std::regex_traits<char> >::_M_match_token (this=this#entry=0x7fffffffe000, token=std::__detail::_ScannerBase::_S_token_subexpr_begin) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:541
#4 0x00007ffff7c513a2 in std::__detail::_Compiler<std::regex_traits<char> >::_M_match_token (token=std::__detail::_ScannerBase::_S_token_subexpr_begin, this=0x7fffffffe000) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:316
#5 std::__detail::_Compiler<std::regex_traits<char> >::_M_atom (this=this#entry=0x7fffffffe000) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:326
#6 0x00007ffff7c515b0 in std::__detail::_Compiler<std::regex_traits<char> >::_M_term (this=0x7fffffffe000) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:136
#7 std::__detail::_Compiler<std::regex_traits<char> >::_M_alternative (this=0x7fffffffe000) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:118
#8 0x00007ffff7c51809 in std::__detail::_Compiler<std::regex_traits<char> >::_M_disjunction (this=this#entry=0x7fffffffe000) at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:97
#9 0x00007ffff7c51e18 in std::__detail::_Compiler<std::regex_traits<char> >::_Compiler (this=0x7fffffffe000, __b=<optimized out>, __e=<optimized out>, __traits=..., __flags=<optimized out>)
at /usr/local/include/c++/4.9.4/bits/regex_compiler.tcc:82
#10 0x00007ffff7c5222d in std::__detail::__compile_nfa<std::regex_traits<char> > (__first=<optimized out>, __last=<optimized out>, __traits=..., __flags=<optimized out>) at /usr/local/include/c++/4.9.4/bits/regex_compiler.h:158
#11 0x00007ffff7c524da in std::basic_regex<char, std::regex_traits<char> >::basic_regex<char const*> (__f=<optimized out>, __last=<optimized out>, __first=<optimized out>, this=0x7ffff7dc2a40 <sql::mariadb::UrlParser::URL_PARAMETER>)
at /usr/local/include/c++/4.9.4/bits/regex.h:540
#12 std::basic_regex<char, std::regex_traits<char> >::basic_regex (this=0x7ffff7dc2a40 <sql::mariadb::UrlParser::URL_PARAMETER>, __p=<optimized out>, __f=<optimized out>) at /usr/local/include/c++/4.9.4/bits/regex.h:452
#13 0x00007ffff7c331ee in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at /home/buildbot/src/src/UrlParser.cpp:34
#14 _GLOBAL__sub_I_UrlParser.cpp(void) () at /home/buildbot/src/src/UrlParser.cpp:444
#15 0x00007ffff7de7dc2 in call_init (l=<optimized out>, argc=argc#entry=1, argv=argv#entry=0x7fffffffe2b8, env=env#entry=0x7fffffffe2c8) at dl-init.c:72
#16 0x00007ffff7de7eb6 in call_init (env=0x7fffffffe2c8, argv=0x7fffffffe2b8, argc=1, l=<optimized out>) at dl-init.c:119
#17 _dl_init (main_map=0x7ffff7ffe130, argc=1, argv=0x7fffffffe2b8, env=0x7fffffffe2c8) at dl-init.c:120
#18 0x00007ffff7dd9f2a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#19 0x0000000000000001 in ?? ()
#20 0x00007fffffffe520 in ?? ()
#21 0x0000000000000000 in ?? ()
(gdb)
Does this help?
Mark
GCC version 7.3.1
In the backtrace, we see that the crash is happening in the GCC-7 regexp implementation:
#1 0x0000000000409155 in std::__detail::_Scanner<char>::_M_scan_normal (this=0x7fffffffe018) at /usr/include/c++/7/bits/regex_scanner.tcc:119
We also see that this crash is happening while some global inside (presumably1) MariaDB connector is being initialized, while using GCC-4.9.4 version of libstdc++:
#12 std::basic_regex<char, std::regex_traits<char> >::basic_regex (this=0x7ffff7dc2a40 <sql::mariadb::UrlParser::URL_PARAMETER>, __p=<optimized out>, __f=<optimized out>) at /usr/local/include/c++/4.9.4/bits/regex.h:452
#13 0x00007ffff7c331ee in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at /home/buildbot/src/src/UrlParser.cpp:34
It is exceedingly likely that this 4.9.4 vs. 7.3.1 mismatch is the cause of the crash, and that either building the app with g++-4.9.4 or building the MariaDB with g++-7.3.1 will fix the problem.
In theory GCC version of libstdc++ should be backwards compatible, but verifying ABI compatibility in C++ is quite hard, and many mistakes have been made. Also, g++4.9.4 is ancient.
Another possible solution is to build the application with clang using libc++ -- this will avoid any possibility of symbol conflicts2.
1 You can verify whether frame #13 is really coming from the MariaDB by executing these GDB commands: frame 13, info symbol $pc.
2 To achieve this, you may need to explicitly tell clang to use libc++, as it may default to using libstdc++. Use clang++ -stdlib=libc++ ... to be sure. Documentation here.
Hy,
i have a gtkmm application, which does some async network-requests, to ask the server for additional properties of the gtk-widgets.
This means for example, that the application should be able to change the label of a widget.
In this example I have created a new widget based on Gtk::ToggleButton.
But I found out that sometimes the gtkmm-application crashes with a segfault. When debuging with gdb I always get the line where i set the label.
For better understanding, I have created a MWE which does the label-changes in a loop, to simulate lots of async-calls:
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <iostream>
#include <thread>
#include <mutex>
#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/togglebutton.h>
class led_label_t : public Gtk::ToggleButton {
public:
using value_list_t = std::vector<Glib::ustring>;
using lock_t = std::lock_guard<std::mutex>;
led_label_t(Glib::ustring label = "<no data>", bool mnemonic = false)
: Gtk::ToggleButton(std::move(label), std::move(mnemonic)),
_values{"SEL1", "SEL2"} {}
protected:
virtual void on_toggled(void) override {
std::cout << "Clicked Button." << std::endl;
lock_t lock(_mtx);
value_changed(_values[get_active()]);
}
virtual void value_changed(Glib::ustring& value) {
std::string path;
if (get_active()) {
path =
"/usr/share/icons/Adwaita/16x16/emblems/emblem-important.png";
} else {
path = "/usr/share/icons/Adwaita/16x16/emblems/emblem-default.png";
}
remove(); // remove previous label
std::cout << "Changed Label of led_label: "
<< ", value: " << value << std::endl;
add_pixlabel(path, value);
}
private:
mutable std::mutex _mtx;
value_list_t _values;
};
int main(void) {
auto app = Gtk::Application::create();
Gtk::Window window;
window.set_default_size(200, 200);
led_label_t inst{};
inst.show();
window.add(inst);
auto f = [&inst, &window]() {
using namespace std::chrono_literals;
boost::asio::io_service io;
{ //wait for startup
boost::asio::steady_timer t{io, 100ms};
t.wait();
}
bool toggle = true;
for (auto i = 0; i < 2000; i++) {
std::cout << "i=" << i << std::endl;
//wait until next simulated button click
boost::asio::steady_timer t{io, 1ms};
t.wait();
inst.set_active(toggle);
toggle = !toggle;
}
};
std::thread c1(f);
std::thread w([&app, &window]() { app->run(window); });
c1.join();
window.hide();
w.join();
return EXIT_SUCCESS;
}
To compile this example, I use following command:
g++ main.cpp -o main `pkg-config --cflags --libs gtkmm-3.0` -Wall -pedantic -Wextra -Werror -Wcast-qual -Wcast-align -Wconversion -fdiagnostics-color=auto -g -O0 -std=c++14 -lboost_system -pthread
I am using GCC 4.9.2 and libgtkmm-3.14 (both standard debian jessie)
The segfault I get is the following:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffe7fff700 (LWP 7888)]
0x00007ffff6288743 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
(gdb) bt
#0 0x00007ffff6288743 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#1 0x00007ffff6288838 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#2 0x00007ffff6267ce9 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#3 0x00007ffff627241b in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#4 0x00007ffff63a1601 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#5 0x00007ffff63a154c in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#6 0x00007ffff63a26b8 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#7 0x00007ffff644d5ff in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#8 0x00007ffff644d9b7 in gtk_widget_realize ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#9 0x00007ffff644dbe8 in gtk_widget_map ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#10 0x00007ffff621c387 in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#11 0x00007ffff626270f in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#12 0x00007ffff46bf474 in ?? ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#13 0x00007ffff46d9087 in g_signal_emit_valist ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#14 0x00007ffff46d99df in g_signal_emit ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#15 0x00007ffff644db99 in gtk_widget_map ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#16 0x00007ffff64506d8 in gtk_widget_set_parent ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#17 0x00007ffff6217a9b in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#18 0x00007ffff79a44eb in Gtk::Container_Class::add_callback(_GtkContainer*, _GtkWidget*) () from /usr/lib/x86_64-linux-gnu/libgtkmm-3.0.so.1
#19 0x00007ffff46c253b in g_cclosure_marshal_VOID__OBJECTv ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#20 0x00007ffff46bf474 in ?? ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#21 0x00007ffff46d9087 in g_signal_emit_valist ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#22 0x00007ffff46d99df in g_signal_emit ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#23 0x00007ffff6261aa5 in gtk_container_add ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#24 0x000000000040b0b5 in led_label_t::value_changed (this=0x7fffffffe2a0,
value=...) at main.cpp:38
#25 0x000000000040afb1 in led_label_t::on_toggled (this=0x7fffffffe2a0)
at main.cpp:24
#26 0x00007ffff7a18af0 in Gtk::ToggleButton_Class::toggled_callback(_GtkToggleButton*) () from /usr/lib/x86_64-linux-gnu/libgtkmm-3.0.so.1
#27 0x00007ffff46bf245 in g_closure_invoke ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#28 0x00007ffff46d083b in ?? ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#29 0x00007ffff46d9778 in g_signal_emit_valist ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#30 0x00007ffff46d99df in g_signal_emit ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#31 0x00007ffff63ecb4d in ?? () from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#32 0x00007ffff798a4a0 in Gtk::Button_Class::clicked_callback(_GtkButton*) ()
from /usr/lib/x86_64-linux-gnu/libgtkmm-3.0.so.1
#33 0x00007ffff46bf474 in ?? ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#34 0x00007ffff46d9087 in g_signal_emit_valist ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#35 0x00007ffff46d99df in g_signal_emit ()
from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#36 0x00007ffff63ec936 in gtk_toggle_button_set_active ()
from /usr/lib/x86_64-linux-gnu/libgtk-3.so.0
#37 0x0000000000405e12 in <lambda()>::operator()(void) const (
__closure=0x74f4f8) at main.cpp:73
#38 0x000000000040811a in std::_Bind_simple<main()::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>) (this=0x74f4f8) at /usr/include/c++/4.9/functional:1700
#39 0x0000000000407fa9 in std::_Bind_simple<main()::<lambda()>()>::operator()(void) (this=0x74f4f8) at /usr/include/c++/4.9/functional:1688
#40 0x0000000000407e9e in std::thread::_Impl<std::_Bind_simple<main()::<lambda()>()> >::_M_run(void) (this=0x74f4e0) at /usr/include/c++/4.9/thread:115
#41 0x00007ffff3f47970 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#42 0x00007ffff37650a4 in start_thread (arg=0x7fffe7fff700)
at pthread_create.c:309
#43 0x00007ffff349a04d in clone ()
at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
Maybe the Interesting line of this is
#24: 0x000000000040b0b5 in led_label_t::value_changed (this=0x7fffffffe2a0,
value=...) at main.cpp:38)
this is the line where add_pixlabel(path, value); is called.
What am I doing wrong here?
Attention:
This segfault doesn't come always, I found out, that on my desktop-machine I get the error once every 10 calls. (Intel i7-3xxx)
And on my laptop I get the error nearly every call (Intel i5-3xxx)
Now I have found a solution, based on the answer of #user4581301. He was right, that gtkmm doesn't support multithreading. (To be more precise, libsigc++ and sigc::trackable are not thread-safe)
However, care is required when writing programs based on gtkmm using
multiple threads of execution, arising from the fact that libsigc++,
and in particular sigc::trackable, are not thread-safe.
Quote from gtkmm documentation.
Therefore I have used Glib::Dispatcher, to execute the set_label() - method in the context of the gtkmm-Main-Loop of the window.
Here is the code, that did not segfault anymore on my machine(s) (even with many retries)
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <cassert>
#include <iostream>
#include <thread>
#include <mutex>
#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/togglebutton.h>
#include <glibmm/dispatcher.h>
#define LOG() \
std::cout << (std::chrono::system_clock::now() - start).count() << " " \
<< std::this_thread::get_id() << ": "
auto start = std::chrono::system_clock::now();
class led_label_t : public Gtk::ToggleButton {
public:
using value_list_t = std::vector<Glib::ustring>;
using lock_t = std::lock_guard<std::mutex>;
using action_queue_t = std::vector<Glib::ustring>;
led_label_t(Glib::ustring label = "<no data>", bool mnemonic = false)
: Gtk::ToggleButton(std::move(label), std::move(mnemonic)),
_values{"SEL1", "SEL2"} {}
void set_dispatcher(Glib::Dispatcher* dp) {
_dp = dp;
_dp->connect([this](void) { dispatcher_task(); });
}
protected:
virtual void on_toggled(void) override {
LOG() << "Clicked Button." << std::endl;
{
lock_t lock(_action_mtx);
auto value = _values[get_active()];
_action_queue.push_back({value});
LOG() << "Added label into queue " << value << std::endl;
if (_action_queue.size() > 1) {
return;
}
}
_dp->emit();
}
void dispatcher_task(void) {
Glib::ustring label;
for (;;) {
{
lock_t lock(_action_mtx);
if (_action_queue.size() == 0) {
return;
}
label = *_action_queue.begin();
_action_queue.erase(_action_queue.begin());
}
set_label(label);
LOG() << "Set the label " << label << std::endl;
}
}
private:
mutable std::mutex _action_mtx;
action_queue_t _action_queue;
value_list_t _values;
Glib::Dispatcher* _dp;
};
int main(void) {
auto app = Gtk::Application::create();
Gtk::Window window;
window.set_default_size(200, 200);
led_label_t inst{};
inst.show();
window.add(inst);
auto f = [&inst, &window]() {
using namespace std::chrono_literals;
boost::asio::io_service io;
{ // wait for startup
boost::asio::steady_timer t{io, 100ms};
t.wait();
}
bool toggle = true;
for (auto i = 0; i < 200000; i++) {
// wait until next simulated button click
boost::asio::steady_timer t{io, 250us};
t.wait();
LOG() << "i=" << i << std::endl;
inst.set_active(toggle);
toggle = !toggle;
LOG() << "finished" << std::endl;
}
};
std::thread c1(f);
std::thread w([&app, &window, &inst]() {
Glib::Dispatcher dp;
inst.set_dispatcher(&dp);
app->run(window);
});
c1.join();
window.hide();
w.join();
return EXIT_SUCCESS;
}
Accessing and changing UI components from multiple threads is always tricky. UIs need to be fast and responsive to user input, so they can't hang around for background tasks to complete. As a result UI components are rarely protected by mutex or other synchronization. You write, it happens. Except when something else gets in the way.
If you write from two threads... Ooops.
You're half way through a write when another thread reads... Ooops.
Say for example Thread 4 is part way through writing a new string into the label when a screen refresh is triggered. If the backend for label is a c-style string, the terminating NULL may have been overwritten and the label write runs off the end into bad RAM.
All sorts of things could go wrong, and some will be survivable or, worse, look like it. You're better off having all of the UI management in one thread and have the other threads queue updates to the UI thread. Start by looking into Model View Controller and then try related patterns if needed.
I'm debugging a process that runs really slow when is executed from gdb on Windows (mingw32), so I decided to run it until it crash without gdb, and then to attach the debugger. I've installed a signal handler for sigsegv that shows its pid and waits, so when I see the message I load gdb and use the "attach" command with that pid. The problem is that gdb shows me an useless backtrace at that point. Here's an example:
void my_sigsegv_handler(int) {
std::cerr << "Segmentation fault! pid=" << GetCurrentProcessId();
std::cin.get(); // wait for gdb
}
int main() {
signal(SIGSEGV,my_sigsegv_handler);
int *p = 0;
std::cout << *p; // boom!
}
Compiled with "mingw32-g++ -g -O0", output from gdb's command "bt" (after selecting the proper thread) is:
#0 0x764e73ea in ?? ()
#1 0x7646f489 in ?? ()
#2 0x75edc3b3 in ?? ()
#3 0x75edc2bc in ?? ()
#4 0x75edc472 in ?? ()
#5 0x00415502 in __gnu_cxx::stdio_sync_filebuf<char, std::char_traits<char> >::uflow() ()
#6 0x00434f32 in std::istream::get() ()
#7 0x004016d5 in my_sigsegv_handler () at C:\Users\usuario\zinjai\sin_titulo.cpp:8
#8 0x004010f9 in _gnu_exception_handler (exception_data=0x28fa88) at ../mingwrt-4.0.3-1-mingw32-src/src/libcrt/crt/crt1.c:137
#9 0x76469d57 in ?? ()
#10 0x77100727 in ?? ()
#11 0x770c9d45 in ?? ()
#12 0x00000000 in ?? ()
Notice that this example does not corrupt stack when generating the segfault. Actualy, I can debug it anyway, just continuing execution. If I press enter the signal handler finishes, returns to the place where it was generated (main function), and the problem is not solved, but this time gdb is there to catch it. But I'd like to now how does it really works.
If I use the same method in gnu/linux I can see what I want to see here:
#5 0x00007f6809bf349e in std::istream::get() () from /usr/lib64/libstdc++.so.6
#6 0x00000000004008cd in my_signal_handler () at /home/zaskar/.zinjai/sin_titulo.cpp:6
#7 <signal handler called>
#8 0x00000000004008f9 in main (argc=1, argv=0x7fffa0613108) at /home/zaskar/.zinjai/sin_titulo.cpp:11
So the question is, why gdb cannot show me the correct backtrace from withing the signal handler? Or what am I doing wrong? Is there any better way to solve it?
I've been doing threaded networking for a game, but the server dies randomly, while i've been testing the networking so that I have several clients connecting and sending bunch of packets and disconnecting then connecting back again.
I am using c++ with SFML/Network and SFML/System threads. I have thread which listens for connections in the server once connection is established it creates two new threads for sending and receiving packets. The event handler and the send/receive threads share data with two std::queues. I've been trying to debug the crash with gdb, but i'm not that experienced with this so i'm looking for help.
Here is gdb console input when the crash happens.
OUT: 10 1 HELLO
IN: 10 0 LOLOLOL
OUT: 10 1 HELLO
IN: 10 0 LOLOLOL
OUT: 10 1 HELLO
Out thread killed by in thread!
In thread died!
New client connected!
[Thread 0x34992b70 (LWP 16167) exited]
[New Thread 0x3118bb70 (LWP 16186)]
terminate called without an active exception
Program received signal SIGABRT, Aborted.
[Switching to Thread 0x35193b70 (LWP 16166)]
0x00110416 in __kernel_vsyscall ()
Here is the backtrace:
(gdb) backtrace
#0 0x00110416 in __kernel_vsyscall ()
#1 0x46a0967f in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2 0x46a0afb5 in abort () at abort.c:92
#3 0x47b8af0d in __gnu_cxx::__verbose_terminate_handler () at ../../../../libstdc++-v3/libsupc++/vterminate.cc:95
#4 0x47b88c84 in __cxxabiv1::__terminate (handler=0x47b8adc0 <__gnu_cxx::__verbose_terminate_handler()>) at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:40
#5 0x47b88cc0 in std::terminate () at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:50
#6 0x47b8878f in __cxxabiv1::__gxx_personality_v0 (version=1, actions=10, exception_class=890844228, ue_header=0x35193dc0, context=0x35192ea0)
at ../../../../libstdc++-v3/libsupc++/eh_personality.cc:669
#7 0x46bdbfbe in _Unwind_ForcedUnwind_Phase2 (exc=0x35193dc0, context=0x35192ea0) at ../../../gcc/unwind.inc:175
#8 0x46bdc3a9 in _Unwind_ForcedUnwind (exc=0x35193dc0, stop=0x46b76fc0 <unwind_stop>, stop_argument=0x35193444) at ../../../gcc/unwind.inc:207
#9 0x46b794e2 in _Unwind_ForcedUnwind (exc=0x35193dc0, stop=0x46b76fc0 <unwind_stop>, stop_argument=0x35193444) at ../nptl/sysdeps/pthread/unwind-forcedunwind.c:132
#10 0x46b77141 in __pthread_unwind (buf=<optimized out>) at unwind.c:130
#11 0x46b6f5bb in __do_cancel () at ../nptl/pthreadP.h:265
#12 sigcancel_handler (sig=<optimized out>, si=<optimized out>, ctx=<optimized out>) at nptl-init.c:202
#13 sigcancel_handler (sig=32, si=0x35192f7c, ctx=0x35192ffc) at nptl-init.c:155
#14 <signal handler called>
#15 0x08049930 in out (data=0xb761c798) at src/layer7.cpp:40
#16 0x0804b8d7 in sf::priv::ThreadFunctorWithArg<void (*)(networkdata*), networkdata*>::Run (this=0xb761c7c8) at /usr/local/include/SFML/System/Thread.inl:48
#17 0x00116442 in sf::Thread::Run() () from /home/toni/ProjectRepos/sfml/build/lib/libsfml-system.so.2
#18 0x001166df in sf::priv::ThreadImpl::EntryPoint(void*) () from /home/toni/ProjectRepos/sfml/build/lib/libsfml-system.so.2
#19 0x46b70c5e in start_thread (arg=0x35193b70) at pthread_create.c:305
#20 0x46ab4b4e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:133
Here is the thread code from src/layer7.cpp
void out(networkdata * data) {
bool running = true;
while(running) {
if(data->pipe_out->pipe_empty() == false) {
sf::Packet packet = data->pipe_out->pop_message();
if(data->socket->Send(packet) == sf::Socket::Disconnected) {
data->thread_in->Terminate();
std::cout << "In thread killed by out thread!" << std::endl;
running = false;
}
}
}
std::cout << "Out thread died!" << std::endl;
}
Line 40 is the first if keyword after the while(running).
The data->pipe_out->pipe_empty() is call to the queue->empty()
The data->pipe_out->pop_message() is call which pops the front from the queue.
Then it sends the packet and checks if the connection is not disconnected
if socket is disconnected it terminates the "in" thread and stops the own thread.
You need locks around data to protect against concurrent access to the same data structure from multiple threads.
One possible reason for is an exception: exception should be caught withing thread. Also, looks like data->thread_in->Terminate() sends cancelation request, make sure that all established cancellation handlers are working correctly in that case.