6

In my application I have a worker thread iterating through a list of files and I'm trying to update the GUI table view to indicate what the current file is. In my worker thread I have the following line when I start a new file:

emit runOnGui([this,row](){ui.tblFiles->selectRow(row);});

where runOnGui is a signal and it's handled via:

connect(this, &MainWindow::runOnGui, [this](function<void()> action){
    action();
    });

My understanding is the action() should now be running on the UI thread but sometimes, not always, this is segfaulting on me. The backtrace looks like:

#0  0x00007ffff537c28a in ?? () from /usr/lib/libQt5Core.so.5
#1  0x00007ffff537cf23 in ?? () from /usr/lib/libQt5Core.so.5
#2  0x00007ffff537d047 in ?? () from /usr/lib/libQt5Core.so.5
#3  0x00007ffff5376943 in QItemSelection::merge(QItemSelection const&, QFlags<QItemSelectionModel::SelectionFlag>) () from /usr/lib/libQt5Core.so.5
#4  0x00007ffff5379e43 in QItemSelectionModel::select(QItemSelection const&, QFlags<QItemSelectionModel::SelectionFlag>) () from /usr/lib/libQt5Core.so.5
#5  0x00007ffff6d97511 in ?? () from /usr/lib/libQt5Widgets.so.5
#6  0x0000000000465193 in MainWindow::<lambda()>::operator()(void) const (__closure=0x7fffda262710) at <...>/MainWindow.cpp:248
#7  0x00000000004682c8 in std::_Function_handler<void(), MainWindow::pushFiles()::<lambda()> >::_M_invoke(const std::_Any_data &) (__functor=...) at /usr/include/c++/6.2.1/functional:1740
#8  0x0000000000472722 in std::function<void ()>::operator()() const (this=0x7fffda262710) at /usr/include/c++/6.2.1/functional:2136
#9  0x0000000000463798 in MainWindow::MainWindow(QWidget*)::{lambda(std::function<void ()>)#1}::operator()(std::function<void ()>) const () at <...>/MainWindow.cpp:34
#10 0x0000000000469a32 in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<std::function<void()> >, void, MainWindow::MainWindow(QWidget*)::<lambda(std::function<void()>)> >::call(MainWindow::<lambda(std::function<void()>)> &, void **) (f=..., arg=0x7fffda2628e0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:501
#11 0x000000000046977b in QtPrivate::Functor<MainWindow::MainWindow(QWidget*)::<lambda(std::function<void()>)>, 1>::call<QtPrivate::List<std::function<void()> >, void>(MainWindow::<lambda(std::function<void()>)> &, void *, void **) (f=..., arg=0x7fffda2628e0) at /usr/include/qt/QtCore/qobjectdefs_impl.h:558
#12 0x0000000000468d8c in QtPrivate::QFunctorSlotObject<MainWindow::MainWindow(QWidget*)::<lambda(std::function<void()>)>, 1, QtPrivate::List<std::function<void()> >, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=1, this_=0x7cc630, r=0x7fffffffe500, a=0x7fffda2628e0, ret=0x0) at /usr/include/qt/QtCore/qobject_impl.h:198
#13 0x00007ffff53ed85e in QMetaObject::activate(QObject*, int, int, void**) () from /usr/lib/libQt5Core.so.5
#14 0x000000000047a669 in MainWindow::runOnGui(std::function<void ()>) (this=0x7fffffffe500, _t1=...) at <...>/moc_MainWindow.cpp:128
#15 0x0000000000465617 in MainWindow::pushFiles (this=0x7fffffffe500) at <...>/MainWindow.cpp:248
#16 0x0000000000476d14 in std::__invoke_impl<void, void (MainWindow::* const&)(), MainWindow*>(std::__invoke_memfun_deref, void (MainWindow::* const&)(), MainWindow*&&) ( __f=@0xa35080: (void (MainWindow::*)(MainWindow * const)) 0x4651ea <MainWindow::pushFiles()>, __t=<unknown type in <...>/simtool, CU 0x16a5cd, DIE 0x1cbd99>) at /usr/include/c++/6.2.1/functional:235
#17 0x0000000000476ca1 in std::__invoke<void (MainWindow::* const&)(), MainWindow*>(void (MainWindow::* const&)(), MainWindow*&&) ( __fn=@0xa35080: (void (MainWindow::*)(MainWindow * const)) 0x4651ea <MainWindow::pushFiles()>, __args#0=<unknown type in <...>/simtool, CU 0x16a5cd, DIE 0x1cbd99>) at /usr/include/c++/6.2.1/functional:260
#18 0x0000000000476c52 in std::_Mem_fn_base<void (MainWindow::*)(), true>::operator()<MainWindow*>(MainWindow*&&) const (this=0xa35080, __args#0=<unknown type in <...>/simtool, CU 0x16a5cd, DIE 0x1cbd99>) at /usr/include/c++/6.2.1/functional:613
#19 0x0000000000476c1d in std::_Bind_simple<std::_Mem_fn<void (MainWindow::*)()> (MainWindow*)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0xa35078) at /usr/include/c++/6.2.1/functional:1400
#20 0x0000000000476b37 in std::_Bind_simple<std::_Mem_fn<void (MainWindow::*)()> (MainWindow*)>::operator()() ( this=0xa35078) at /usr/include/c++/6.2.1/functional:1389
#21 0x0000000000476ad6 in std::thread::_State_impl<std::_Bind_simple<std::_Mem_fn<void (MainWindow::*)()> (MainWindow*)> >::_M_run() (this=0xa35070) at /usr/include/c++/6.2.1/thread:196
#22 0x00007ffff4e6e31f in std::execute_native_thread_routine (__p=0xa35070) at /build/gcc/src/gcc/libstdc++-v3/src/c++11/thread.cc:83
#23 0x00007ffff743f454 in start_thread () from /usr/lib/libpthread.so.0
#24 0x00007ffff45e27df in clone () from /usr/lib/libc.so.6

Any ideas why this segfault is happening?

Misc info:

  • Qt 5.7
  • Arch Linux 4.7.4
  • gcc 6.2.1
ryan0270
  • 1,135
  • 11
  • 33
  • 1
    I am not sure you can use lambdas or `std::function` as parameters for signals. I would guess that is your issue here. This also looks like a strange design in the first place. – Hayt Oct 27 '16 at 13:58
  • Would qt complain about that, though? I've had other cases where qt complains at runtime about unregisterd types where I had to use qRegisterMetaType. I kind of thought that might happen in this case too but it never complained. – ryan0270 Oct 27 '16 at 14:06
  • I have no clue to be honest. the documentation does not restrict it to any type so it may work. But it still looks kind of weird. maybe instead of using the closure-type directly cast it to a `std::function` object before and give this as a parameter. Though I assume this will be done because the std::function is probably in the signal signature. – Hayt Oct 27 '16 at 14:12
  • Actually, you were mostly right I think but because of the connect function signature I used it was printing that warning. Using Kevin's answer as a starting point got the runtime message I. – ryan0270 Oct 27 '16 at 14:28
  • @Hayt How is a functor a "parameter" for a signal here? Qt 5 supports connecting to functors. – Kuba hasn't forgotten Monica Oct 27 '16 at 14:51
  • @KubaOber as i said I was not sure. But he emits a signal with a lambda as parameter `emit signal()`. If this syntax does something else if you input a closure class / `std::function` object I am not aware of this. (that is why I put it in the comments anyway). If you have some resources on this I will be glad to read about it. Always looking forward to learn new things. :) – Hayt Oct 27 '16 at 14:54
  • Sorry for the noise: yes, the functor is a parameter, I missed that :( – Kuba hasn't forgotten Monica Oct 27 '16 at 14:55

1 Answers1

9

The automatism for crossing thread boundaries in signal/slot connections requires a receiver object, so that the emitting code can check if the current thread is equal or different to the thread of the receiver.

You could try the connect variant that has a reference object as the third argument or calling the slot with QMetaObject::invokeMethod() and explicitly stating Qt::QueuedConnection as the connection type.

E.g.

QMetaObject::invokeMethod(ui.tblFiles, "selectRow", Qt::QueuedConnection, Q_ARG(int, row));
Kevin Krammer
  • 5,159
  • 2
  • 9
  • 22
  • Setting up the connection via this method highlighted that qt does NOT automatically know how to pass std::function objects around. So I'm creating a GuiAction wrapper that that I can register with the meta type system to hopefully work. – ryan0270 Oct 27 '16 at 14:27
  • `invokeMethod` incurs the overhead of a string-indexed method lookup. It works, but there's no need to prematurely pessimize like that. – Kuba hasn't forgotten Monica Oct 27 '16 at 14:53
  • @ryan0270 for an automatic thread-crossing connection the arguments of the signal need to be stored in `QVariant` as the mechanism uses an internal custom event to send the data to the receiving object's thread's event loop. Any custom type needs to be marked with `Q_DECLARE_METATYPE` and registerd with `qRegisterMetaType()` for this to work. – Kevin Krammer Oct 27 '16 at 17:48