I know that you need to reimplement the QApplication::notify() method to properly catch exceptions thrown from the main thread's event handlers.
But what about other threads? Say, I have an object with a slot, and this object lives in a QThread (with default run() method, which just calls exec()), i.e. the thread affinity of this object is the background QThread. So, where should I catch exceptions thrown from this object's slot?
IOW, how do I reimplement the notify() method of a background thread?
When you create your Custom Application with overriden notify method; the QThread you create uses this overriden method too (as the main thread) once it has started its own event loop
It means in practice that if you connect any slot to the QThread::started signal; then this slot executes outside the event loop of thethread, thus not in the overriden notify method.
Here is a sample of code that helps to understand what happen:
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
#include <exception>
class ThrowingObject : public QObject
{
public:
void doThrowInNotify()
{
qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing";
throw std::exception("KBOOOM");
}
void scheduleThrow()
{
QTimer* singleShot = new QTimer(this);
singleShot->setSingleShot(true);
connect(singleShot, &QTimer::timeout, this, &ThrowingObject::doThrow);
singleShot->start();
qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I will throw in run";
}
void doThrow()
{
qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing right now";
//This exception is not catched, and definitly crash the process
throw std::exception("KBOOOM");
}
void doThrowOutsideNotify()
{
//wait 5s for demo purpose, this slot is called by Object2, after Object1 throw in thread1 event loop
QThread::sleep(5);
qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing right now";
//This exception is not catched, and definitly crash the process
throw std::exception("FATAL KBOOOM");
}
};
class ApplicationWithExceptionCatchedInNotify : public QApplication
{
public:
ApplicationWithExceptionCatchedInNotify(int argc, char *argv[]) :
QApplication(argc,argv)
{}
bool notify(QObject* receiver, QEvent *e) override
{
try {
return QApplication::notify(receiver, e);
}
catch(std::runtime_error e)
{
qDebug() << "std::runtime_error in thread : " << QThread::currentThreadId();
qDebug() << e.what();
}
catch(std::exception e)
{
qDebug() << "std::exception in thread : " << QThread::currentThreadId();
qDebug() << e.what();
}
catch(...)
{
qDebug() << "exception thread : " << QThread::currentThreadId();
}
qDebug() << "catch in notify ";
return false;
}
};
int main(int argc, char *argv[])
{
ApplicationWithExceptionCatchedInNotify app(argc, argv);
qDebug() << "Main QThread id" << QThread::currentThreadId();
//Object o1 will throw in its event loop (in notify)
QThread thread1;
ThrowingObject o1;
o1.moveToThread(&thread1);
QObject::connect(&thread1, &QThread::started, &o1, &ThrowingObject::scheduleThrow);
thread1.start();
//Object o2 will throw before the event loop is installed
QThread thread2;
ThrowingObject o2;
o2.moveToThread(&thread2);
//Connect to started signal.
QObject::connect(&thread2, &QThread::started, &o2, &ThrowingObject::doThrowOutsideNotify);
thread2.start();
app.exec();
}
Running this code sample on windows , Qt 5.9 with Qt creator gives for example :
Output :
Main QThread id 0x11e4
I am execution on thread id 0x180c and I will throw in run
I am execution on thread id 0x180c and I am throwing right now
std::exception in thread : 0x180c
KBOOOM
catch in notify
I am execution on thread id 0x27b8 and I am throwing right now
and a Microsoft Visual Studio Runtime Library pop up crying that :
Microsoft Visual C++ Runtime Library
Debug Error!
Program: ...ad-Desktop_Qt_5_9_2_MSVC2017_64bit-Debug\debug\DemoThread.exe
abort() has been called
(Press Retry to debug the application)
If you put breakpoint; once can realize that :
Object o1 throws in QThread::exec methods, going down the callstack we can see we are in ApplicationWithExceptionCatchedInNotify::Notify
1 ThrowingObject::doThrow main.cpp 35 0x7ff66615352b
2 QtPrivate::FunctorCall,QtPrivate::List<>,void,void (__cdecl ThrowingObject:: *)(void) __ptr64>::call qobjectdefs_impl.h 136 0x7ff66615358c
3 QtPrivate::FunctionPointer::call,void> qobjectdefs_impl.h 170 0x7ff666152ce7
4 QtPrivate::QSlotObject,void>::impl qobject_impl.h 121 0x7ff66615363e
5 QtPrivate::QSlotObjectBase::call qobject_impl.h 101 0x54a82428
6 QMetaObject::activate qobject.cpp 3754 0x54a70ee0
7 QMetaObject::activate qobject.cpp 3629 0x54a707a8
8 QTimer::timeout moc_qtimer.cpp 202 0x54a8f739
9 QTimer::timerEvent qtimer.cpp 257 0x54a8f79a
10 QObject::event qobject.cpp 1228 0x54a72b73
11 QApplicationPrivate::notify_helper qapplication.cpp 3722 0x53aeb8ee
12 QApplication::notify qapplication.cpp 3094 0x53ae6323
13 ApplicationWithExceptionCatchedInNotify::notify main.cpp 60 0x7ff666158730
14 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1018 0x54a1b0c6
15 QCoreApplication::sendEvent qcoreapplication.h 233 0x54a26062
16 QEventDispatcherWin32::event qeventdispatcher_win.cpp 1041 0x54ad8cab
17 QApplicationPrivate::notify_helper qapplication.cpp 3722 0x53aeb8ee
18 QApplication::notify qapplication.cpp 3094 0x53ae6323
19 ApplicationWithExceptionCatchedInNotify::notify main.cpp 60 0x7ff666158730
20 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1018 0x54a1b0c6
21 QCoreApplication::sendEvent qcoreapplication.h 233 0x54a26062
22 QCoreApplicationPrivate::sendPostedEvents qcoreapplication.cpp 1678 0x54a1c982
23 QEventDispatcherWin32::sendPostedEvents qeventdispatcher_win.cpp 1064 0x54ad8e6a
24 qt_internal_proc qeventdispatcher_win.cpp 237 0x54ad6b47
25 CallWindowProcW USER32 0x7ffba1571c24
26 DispatchMessageW USER32 0x7ffba157156c
27 QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp 628 0x54ad755b
28 QEventLoop::processEvents qeventloop.cpp 135 0x54a15498
29 QEventLoop::exec qeventloop.cpp 212 0x54a156de
30 QThread::exec qthread.cpp 515 0x5465028f
31 QThread::run qthread.cpp 583 0x546501c3
32 QThreadPrivate::start qthread_win.cpp 380 0x5465caed
33 BaseThreadInitThunk KERNEL32 0x7ffb9f5b8364
34 RtlUserThreadStart ntdll 0x7ffba1bb7091
Object o2 throws in QThread::start methods; outside of the thread2 event loop
1 ThrowingObject::doThrowOutsideNotify main.cpp 45 0x7ff666152ba6
2 QtPrivate::FunctorCall,QtPrivate::List<>,void,void (__cdecl ThrowingObject:: *)(void) __ptr64>::call qobjectdefs_impl.h 136 0x7ff66615358c
3 QtPrivate::FunctionPointer::call,void> qobjectdefs_impl.h 170 0x7ff666152ce7
4 QtPrivate::QSlotObject,void>::impl qobject_impl.h 121 0x7ff66615363e
5 QtPrivate::QSlotObjectBase::call qobject_impl.h 101 0x54a82428
6 QMetaObject::activate qobject.cpp 3754 0x54a70ee0
7 QMetaObject::activate qobject.cpp 3629 0x54a707a8
8 QThread::started moc_qthread.cpp 160 0x54650149
9 QThreadPrivate::start qthread_win.cpp 377 0x5465cad6
10 BaseThreadInitThunk KERNEL32 0x7ffb9f5b8364
11 RtlUserThreadStart ntdll 0x7ffba1bb7091
Since certain version of Qt5, the QCoreApplication::notify manual says:
this function is called for all events sent to any object in any
thread.
So this means that the correct answer to my original question was "just do what you were doing all along".
Funnily, just as I discovered this, this newly discovered knowledge instantly became outdated by this phrase from the very same manual:
This function will not be called for objects that live outside the
main thread in Qt 6. Applications that need that functionality should
find other solutions for their event inspection needs in the meantime.
The change may be extended to the main thread, causing this function
to be deprecated.
LOL. I have not migrated to Qt6 yet so I have no idea what's the situation there, but when I do, I might just re-open this exact question.
Related
So I am using WebEngineView in my QML like this:
...
Loader {
// some properties
sourceComponent: WebEngineView {
...
}
}
In the c++ logic I am using QQuickWebEngineProfile::defaultProfile() in the constructor and destructor
MainViewModel(QObject* parent) : QObject(parent)
{
// using QQuickWebEngineProfile::defaultProfile();
// getting cookieStore of the profile and connect it to some slots
}
~MainViewModel()
{
// using QQuickWebEngineProfile::defaultProfile();
// getting cookieStore of the profile and disconnect it from MainViewModel
}
So it's working perfectly, but when I am trying to close my app (calling qApp->quit()), it crashes. If I remove WebEngineView from QML it works, if I remove using of defaultProfile() in c++ it works. But I need these things.
Dump:
1 _threadid ucrtbased 0x7ffb48687c75
2 _threadid ucrtbased 0x7ffb48687e13
3 abort ucrtbased 0x7ffb4869e01d
4 `anonymous namespace'::messageHandler application.cpp 46 0x7ff7c2d2b1bf
5 qt_message_print qlogging.cpp 1844 0x7ffb08317fdf
6 qt_message qlogging.cpp 379 0x7ffb08318657
7 QMessageLogger::fatal qlogging.cpp 890 0x7ffb08316612
8 qt_assert qglobal.cpp 3354 0x7ffb08307a48
9 QAccessible::registerAccessibleInterface qaccessible.cpp 747 0x7ffb01df22bd
10 QAccessible::uniqueId qaccessible.cpp 767 0x7ffb01df2247
11 QQuickWebEngineViewPrivate::widgetChanged qquickwebengineview.cpp 982 0x7ffb4b2b5bda
12 QQuickWebEngineViewPrivate::bindViewAndWidget qquickwebengineview.cpp 972 0x7ffb4b2b5b6e
13 QQuickWebEngineViewPrivate::releaseProfile qquickwebengineview.cpp 200 0x7ffb4b2b2349
14 QtWebEngineCore::ProfileAdapter::~ProfileAdapter profile_adapter.cpp 127 0x7ffad4237f20
15 QtWebEngineCore::ProfileAdapter::`vector deleting destructor' Qt5WebEngineCored 0x7ffad4238678
16 std::default_delete<QtWebEngineCore::DevToolsFrontendQt::NetworkResourceLoader>::operator() memory 1758 0x7ffad41d9a75
17 std::unique_ptr<QtWebEngineCore::WebChannelIPCTransportHost,std::default_delete<QtWebEngineCore::WebChannelIPCTransportHost>>::reset memory 1910 0x7ffad4287857
18 QtWebEngineCore::WebEngineContext::destroy web_engine_context.cpp 339 0x7ffad4295a87
19 QtWebEngineCore::WebEngineContext::destroyContextPostRoutine web_engine_context.cpp 425 0x7ffad4295bf8
20 qt_call_post_routines qcoreapplication.cpp 336 0x7ffb087276ef
21 QApplication::~QApplication qapplication.cpp 714 0x7ffb07767046
...
30 main main.cpp 63 0x7ff7c21d4640
It tried to access main window which at that moment has already been deleted. To resolve this problem I had to delay destruction of my main window. I've done it with
QObject::deleteLater()
I have a code with a QDoubleSpinBox
ui->doubleSpinBoxExposure->setMinimum(0.001);
ui->doubleSpinBoxExposure->setMaximum(1000);
ui->doubleSpinBoxExposure->setSingleStep(1.0);
connect(ui->doubleSpinBoxExposure, SIGNAL(valueChanged(double)),
this, SLOT(OndoubleSpinBoxExposure_valueChanged(double)));
void WidgetCameraParameter::OndoubleSpinBoxExposure_valueChanged(double value)
{
if (!camera)
return;
if (camera->isOpen())
{
float exposure = static_cast<float>(value);
float cameraExposure;
camera->setExposure(exposure);
LOG_INFO() <<" setting exposure to " << value << " ms";
cameraExposure = camera->exposure();
LOG_INFO() <<" resulting exposure is " << cameraExposure << " ms";
}
}
The problem is, when I step up in the gui or down, this happens twice.
The starting parameter is value = 2. StepUp calls this function with 3, and directly afterwards with 4. And I have no idea why.
The stack trace is not helpfull:
1 WidgetCameraParameter::OndoubleSpinBoxExposure_valueChanged widgetcameraparameter.cpp 311 0x406c17
2 WidgetCameraParameter::qt_static_metacall moc_widgetcameraparameter.cpp 110 0x40811f
3 QMetaObject::activate qobject.cpp 3771 0x12bc2e1
4 QMetaObject::activate qobject.cpp 3633 0x12bc575
5 QDoubleSpinBox::valueChanged moc_qspinbox.cpp 436 0x15e66190
6 QDoubleSpinBoxPrivate::emitSignals qspinbox.cpp 1112 0x15e663b2
7 QAbstractSpinBoxPrivate::setValue qabstractspinbox.cpp 1741 0x15e6174d
8 QAbstractSpinBox::stepBy qabstractspinbox.cpp 643 0x15e62aba
9 QAbstractSpinBox::timerEvent qabstractspinbox.cpp 1246 0x15e5ffea
10 QObject::event qobject.cpp 1232 0x12bc918
11 QWidget::event qwidget.cpp 9347 0x15d0c544
12 QAbstractSpinBox::event qabstractspinbox.cpp 795 0x15e65930
13 QApplicationPrivate::notify_helper qapplication.cpp 3727 0x15cc85ca
14 QApplication::notify qapplication.cpp 3690 0x15cd1f4f
15 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1048 0x1295119
16 QCoreApplication::sendEvent qcoreapplication.h 234 0x12e4d87
17 QEventDispatcherWin32Private::sendTimerEvent qeventdispatcher_win.cpp 447 0x12e4d87
18 qt_internal_proc(HWND__ *, unsigned int, unsigned int, long) *16 qeventdispatcher_win.cpp 242 0x12e53d5
19 gapfnScSendMessage 0x771162fa
20 ?? 0x5c0f30
21 USER32!GetThreadDesktop 0x77116d3a
22 QEventDispatcherWin32Private::sendTimerEvent qeventdispatcher_win.cpp 456 0x12e4dc9
23 ?? 0x5c0f30
24 USER32!CharPrevW 0x771177c4
25 USER32!DispatchMessageW 0x7711788a
26 QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp 629 0x12e4ae8
27 QWindowsGuiEventDispatcher::processEvents qwindowsguieventdispatcher.cpp 74 0x2496dab7
28 QEventLoop::processEvents qeventloop.cpp 136 0x12937c8
29 QEventLoop::exec qeventloop.cpp 214 0x1293c20
30 QCoreApplication::exec qcoreapplication.cpp 1336 0x129c30e
31 QGuiApplication::exec qguiapplication.cpp 1761 0x8461552
32 QApplication::exec qapplication.cpp 2901 0x15cc84a9
33 qMain main.cpp 28 0x40183d
34 WinMain *16 qtmain_win.cpp 104 0x4094c5
35 main 0x4179ad
Any idea how to debug this further?
EDIT:
This only happens when I debug with breakpoints in the slot. Without the slot is only called once.
The second call of the slot does not happen from any function within the slot function, but only after the slot has ended from the event loop.
You can loop at the complete code:
https://github.com/pospiech/code/tree/master/libdev/devices/CameraViewer
Looking at the QStyle::StyleHint enum, there is an interesting SH_SpinBox_ClickAutoRepeatThreshold constant. You can check its current value for your spin box, like this:
qDebug() << ui->doubleSpinBoxExposure->style()->styleHint(QStyle::SH_SpinBox_ClickAutoRepeatThreshold);
This generally returns 500, which is the number of milliseconds after which the auto repeat gets triggered (i.e. if the user holds the mouse press on the up spin button for longer than that threshold, the spin box value will start increasing continuously).
To see if you have a timing issue, try changing that value, using a custom QStyle class like this:
#include <QProxyStyle>
class MyStyle : public QProxyStyle
{
public:
int styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const
{
if(stylehint == QStyle::SH_SpinBox_ClickAutoRepeatThreshold)
{
return 2000; //2 seconds threshold
}
return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
}
};
and setting an instance of it to the spin box style:
ui->doubleSpinBoxExposure->setStyle(new MyStyle());
Now it takes a lot (two long seconds) before the auto repeat gets triggered, and your issue should be gone, accordingly.
It looks like you have a freezing gui-thread with your slot.
You can try this code in your slot
void WidgetCameraParameter::OndoubleSpinBoxExposure_valueChanged(double value)
{
#define NUM_LOOPS 1000000000
qDebug() << value;
quint64 i = NUM_LOOPS;
while(i--);
}
To avoid this, you have to move to another thread an operation that consumes a lot of CPU time.
In debug mode its because of autorepeating timer.
Try this code to disable autorepeating in debug and, i think, you`ll understand:
*.h
...
#ifdef QT_DEBUG
bool eventFilter(QObject *watched, QEvent *event) override;
#endif
...
*.c
...
ui->setupUi(this);
#ifdef QT_DEBUG
ui->doubleSpinBoxExposure->installEventFilter(this);
#endif
...
#ifdef QT_DEBUG
bool WidgetCameraParameter::eventFilter(QObject *watched, QEvent *event)
{
QDoubleSpinBox *castSBox = static_cast<QDoubleSpinBox*>(watched);
if(castSBox && event->type()==QEvent::Timer)
{
QTimerEvent *tEvent = static_cast<QTimerEvent*>(event);
if(tEvent)
qDebug() << "<--QEvent::Timer-->" << tEvent->timerId();
return true;
}
return QObject::eventFilter(watched,event);
}
#endif
I'm working on a simple class which upon creation schedules a periodic timer for invoking one of its' methods. The method is virtual, so that derived classes can overload it with whatever periodic work they need.
In my test of this class, however, I randomly experience segmentation fault and can't figure out why. Here's the code and example of good and bad outputs:
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/chrono.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/atomic.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>
//******************************************************************************
class PeriodicImpl;
class Periodic {
public:
Periodic(boost::asio::io_service& io, unsigned int periodMs);
~Periodic();
virtual unsigned int periodicInvocation() = 0;
private:
boost::shared_ptr<PeriodicImpl> pimpl_;
};
//******************************************************************************
class PeriodicImpl : public boost::enable_shared_from_this<PeriodicImpl>
{
public:
PeriodicImpl(boost::asio::io_service& io, unsigned int periodMs,
boost::function<unsigned int(void)> workFunc);
~PeriodicImpl();
void setupTimer(unsigned int intervalMs);
boost::atomic<bool> isRunning_;
unsigned int periodMs_;
boost::asio::io_service& io_;
boost::function<unsigned int(void)> workFunc_;
boost::asio::steady_timer timer_;
};
//******************************************************************************
Periodic::Periodic(boost::asio::io_service& io, unsigned int periodMs):
pimpl_(boost::make_shared<PeriodicImpl>(io, periodMs, boost::bind(&Periodic::periodicInvocation, this)))
{
std::cout << "periodic ctor " << pimpl_.use_count() << std::endl;
pimpl_->setupTimer(periodMs);
}
Periodic::~Periodic()
{
std::cout << "periodic dtor " << pimpl_.use_count() << std::endl;
pimpl_->isRunning_ = false;
pimpl_->timer_.cancel();
std::cout << "periodic dtor end " << pimpl_.use_count() << std::endl;
}
//******************************************************************************
PeriodicImpl::PeriodicImpl(boost::asio::io_service& io, unsigned int periodMs,
boost::function<unsigned int(void)> workFunc):
isRunning_(true),
io_(io), periodMs_(periodMs), workFunc_(workFunc), timer_(io_)
{
}
PeriodicImpl::~PeriodicImpl()
{
std::cout << "periodic impl dtor" << std::endl;
}
void
PeriodicImpl::setupTimer(unsigned int intervalMs)
{
std::cout << "schedule new " << intervalMs << std::endl;
boost::shared_ptr<PeriodicImpl> self(shared_from_this());
timer_.expires_from_now(boost::chrono::milliseconds(intervalMs));
timer_.async_wait([self, this](const boost::system::error_code& e){
std::cout << "hello invoke" << std::endl;
if (!e)
{
if (isRunning_)
{
std::cout << "invoking" << std::endl;
unsigned int nextIntervalMs = workFunc_();
if (nextIntervalMs)
setupTimer(nextIntervalMs);
}
else
std::cout << "invoke not running" << std::endl;
}
else
std::cout << "invoke cancel" << std::endl;
});
std::cout << "scheduled " << self.use_count() << std::endl;
}
//******************************************************************************
class PeriodicTest : public Periodic
{
public:
PeriodicTest(boost::asio::io_service& io, unsigned int periodMs):
Periodic(io, periodMs), periodMs_(periodMs), workCounter_(0){}
~PeriodicTest(){
std::cout << "periodic test dtor" << std::endl;
}
unsigned int periodicInvocation() {
std::cout << "invocation " << workCounter_ << std::endl;
workCounter_++;
return periodMs_;
}
unsigned int periodMs_;
unsigned int workCounter_;
};
//******************************************************************************
void main()
{
boost::asio::io_service io;
boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(io));
boost::thread t([&io](){
io.run();
});
unsigned int workCounter = 0;
{
PeriodicTest p(io, 50);
boost::this_thread::sleep_for(boost::chrono::milliseconds(550));
workCounter = p.workCounter_;
}
work.reset();
//EXPECT_EQ(10, workCounter);
}
Good output:
hello invoke
invoking
invocation 9
schedule new 50
scheduled 5
periodic test dtor
periodic dtor 2
periodic dtor end 2
hello invoke
invoke cancel
periodic impl dtor
Bad output:
hello invoke
invoking
invocation 9
schedule new 50
scheduled 5
periodic test dtor
periodic dtor 2
periodic dtor end 2
periodic impl dtor
Segmentation fault: 11
Apparently, segmentation fault is happening because PeriodicImpl is destructed so as its' timer timer_. But timer is still scheduled - and this leads to SEGFAULT. I can't understand why PeriodicImpl destructor is called in this case, because a shared_ptr to PeriodicImpl was copied to lambda passed as the timer's handler function during setupTimer call and this should've retained a copy of PeriodicImpl and prevent destructor invocation.
Any ideas?
The problem turned out to be entirely not in the questioned code, but in the code that tested it.
I enabled saving core dump file by running ulimit -c unlimited and then used lldb to read it:
$ lldb bin/tests/test-segment-controller -c /cores/core.75876
(lldb) bt all
* thread #1: tid = 0x0000, 0x00007fff8eb800f9 libsystem_malloc. dylib`szone_malloc_should_clear + 2642, stop reason = signal SIGSTOP
* frame #0: 0x00007fff8eb800f9 libsystem_malloc.dylib`szone_malloc_should_clear + 2642
frame #1: 0x00007fff8eb7f667 libsystem_malloc.dylib`malloc_zone_malloc + 71
frame #2: 0x00007fff8eb7e187 libsystem_malloc.dylib`malloc + 42
frame #3: 0x00007fff9569923e libc++abi.dylib`operator new(unsigned long) + 30
frame #4: 0x000000010da4b516 test-periodic`testing::Message::Message( this=0x00007fff521e8450) + 38 at gtest.cc:946
frame #5: 0x000000010da4a645 test-periodic`testing::Message::Message( this=0x00007fff521e8450) + 21 at gtest.cc:946
frame #6: 0x000000010da6c027 test-periodic`std::string testing::internal::StreamableToString<long long>(streamable=0x00007fff521e84b0) + 39 at gtest-message.h:244
frame #7: 0x000000010da558e8 test- periodic`testing::internal::PrettyUnitTestResultPrinter::OnTestEnd( this=0x00007fe733421570, test_info=0x00007fe7334211c0) + 216 at gtest.cc:3141
frame #8: 0x000000010da56a28 test- periodic`testing::internal::TestEventRepeater::OnTestEnd( this=0x00007fe733421520, parameter=0x00007fe7334211c0) + 136 at gtest.cc:3321
frame #9: 0x000000010da53957 test-periodic`testing::TestInfo::Run( this=0x00007fe7334211c0) + 343 at gtest.cc:2667
frame #10: 0x000000010da540c7 test-periodic`testing::TestCase::Run( this=0x00007fe733421660) + 231 at gtest.cc:2774
frame #11: 0x000000010da5b5d6 test- periodic`testing::internal::UnitTestImpl::RunAllTests(this=0x00007fe733421310) + 726 at gtest.cc:4649
frame #12: 0x000000010da83263 test-periodic`bool testing::internal::HandleSehExceptionsInMethodIfSupported< testing::internal::UnitTestImpl, bool>(object=0x00007fe733421310, method=0x000000010da5b300, location="auxiliary test code (environments or event listeners)")(), char const*) + 131 at gtest.cc:2402
frame #13: 0x000000010da6cde1 test-periodic`bool testing::internal::HandleExceptionsInMethodIfSupported< testing::internal::UnitTestImpl, bool>(object=0x00007fe733421310, method=0x000000010da5b300, location="auxiliary test code (environments or event listeners)")(), char const*) + 113 at gtest.cc:2438
frame #14: 0x000000010da5b2a2 test-periodic`testing::UnitTest::Run( this=0x000000010dab18e8) + 210 at gtest.cc:4257
frame #15: 0x000000010da19541 test-periodic`RUN_ALL_TESTS() + 17 at gtest. h:2233
frame #16: 0x000000010da1818b test-periodic`main(argc=1, argv=0x00007fff521e88b8) + 43 at test-periodic.cc:57
frame #17: 0x00007fff9557b5c9 libdyld.dylib`start + 1
frame #18: 0x00007fff9557b5c9 libdyld.dylib`start + 1
thread #2: tid = 0x0001, 0x00007fff8ab404cd libsystem_pthread. dylib`_pthread_mutex_lock + 23, stop reason = signal SIGSTOP
frame #0: 0x00007fff8ab404cd libsystem_pthread.dylib`_pthread_mutex_lock + 23
frame #1: 0x000000010da1c8d5 test- periodic`boost::asio::detail::posix_mutex::lock(this=0x0000000000000030) + 21 at posix_mutex.hpp:52
frame #2: 0x000000010da1c883 test-periodic`boost::asio::detail::scoped_lock< boost::asio::detail::posix_mutex>::scoped_lock(this=0x000000010e4fac38, m=0x0000000000000030) + 51 at scoped_lock.hpp:46
frame #3: 0x000000010da1c79d test-periodic`boost::asio::detail::scoped_lock< boost::asio::detail::posix_mutex>::scoped_lock(this=0x000000010e4fac38, m=0x0000000000000030) + 29 at scoped_lock.hpp:45
frame #4: 0x000000010da227a7 test- periodic`boost::asio::detail::kqueue_reactor::run(this=0x0000000000000000, block=true, ops=0x000000010e4fbda8) + 103 at kqueue_reactor.ipp:355
frame #5: 0x000000010da2223c test- periodic`boost::asio::detail::task_io_service::do_run_one( this=0x00007fe733421900, lock=0x000000010e4fbd60, this_thread=0x000000010e4fbd98, ec=0x000000010e4fbe58) + 348 at task_io_service .ipp:368
frame #6: 0x000000010da21e9f test- periodic`boost::asio::detail::task_io_service::run(this=0x00007fe733421900, ec=0x000000010e4fbe58) + 303 at task_io_service.ipp:153
frame #7: 0x000000010da21d51 test-periodic`boost::asio::io_service::run( this=0x00007fff521e8338) + 49 at io_service.ipp:59
frame #8: 0x000000010da184b8 test- periodic`TestPeriodic_TestDestructionDifferentThread_Test::TestBody( this=0x00007fe733421e28)::$_0::operator()() const + 24 at test-periodic.cc:41
frame #9: 0x000000010da1846c test-periodic`boost::detail::thread_data< TestPeriodic_TestDestructionDifferentThread_Test::TestBody()::$_0>::run( this=0x00007fe733421c10) + 28 at thread.hpp:117
frame #10: 0x000000010da8849c test-periodic`boost::(anonymous namespace) ::thread_proxy(param=<unavailable>) + 124 at thread.cpp:164
frame #11: 0x00007fff8ab4305a libsystem_pthread.dylib`_pthread_body + 131
frame #12: 0x00007fff8ab42fd7 libsystem_pthread.dylib`_pthread_start + 176
frame #13: 0x00007fff8ab403ed libsystem_pthread.dylib`thread_start + 13
Apparently, thread 2 causes crash as it tries to lock mutex which is already destructed. However, I'm not using any mutexes, so this must be something internal to io_service. This might happen if io_service is still being used after its' destruction. Looking closely at my main() function I noticed that the thread t I created is left dangling, i.e. there is no join() call on it. Consequently, this sometimes creates a situation when io object is already destructed (at the end of main) but thread t still tries to use it.
Thus, the problem was fixed by adding t.join() call at the end of main() function:
void main()
{
boost::asio::io_service io;
boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(io));
boost::thread t([&io](){
io.run();
});
unsigned int workCounter = 0;
{
PeriodicTest p(io, 50);
boost::this_thread::sleep_for(boost::chrono::milliseconds(550));
workCounter = p.workCounter_;
}
work.reset();
t.join();
//EXPECT_EQ(10, workCounter);
}
I run your program.Regreattably,I falied to compile.
I add to your program,and modify the following code:
timer_.expires_from_now(boost::chrono::milliseconds(intervalMs));
Modified:
timer_.expires_from_now(std::chrono::milliseconds(intervalMs));
So,I get the same result as your "Good output",and don't get the same result as your "Bad output".
I created a simple program to measure thread performance. I ripped out portions of a larger program in order to illustrate my point. Hopefully it's not too terrible to read.
Here is the program:
#include <sstream>
#include <thread>
#include <list>
#include <map>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <string.h>
std::mutex m_totalTranMutex;
int m_totalTrans = 0;
bool m_startThreads = false;
std::condition_variable m_allowThreadStart;
std::mutex m_threadStartMutex;
std::map<int,std::thread::native_handle_type> m_threadNativeHandles;
char *my_strdup(const char *str)
{
size_t len = strlen(str);
char *x = (char *)malloc(len+1);
if(x == nullptr)
return nullptr;
memcpy(x,str,len+1);
return x;
}
void DoWork()
{
char abc[50000];
char *s1, *s2;
std::strcpy(abc, "12345");
std::strcpy(abc+20000, "12345");
s1 = my_strdup(abc);
s2 = my_strdup(abc);
free(s1);
free(s2);
}
void WorkerThread(int threadID)
{
{
std::unique_lock<std::mutex> lk(m_threadStartMutex);
m_allowThreadStart.wait(lk, []{return m_startThreads;});
}
double transPerSec = 1 / 99999;
int transactionCounter = 0;
int64_t clockTicksUsed = 0;
std::thread::native_handle_type handle = m_threadNativeHandles[threadID];
std::chrono::high_resolution_clock::time_point current = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point end = start + std::chrono::minutes(1);
int random_num_loops = 0;
double interarrivaltime = 0.0;
double timeHolderReal = 0.0;
while(current < end)
{
std::chrono::high_resolution_clock::time_point startWork = std::chrono::high_resolution_clock::now();
for(int loopIndex = 0; loopIndex < 100; ++loopIndex)
{
for(int alwaysOneHundred = 0; alwaysOneHundred < 100; ++alwaysOneHundred)
{
DoWork();
}
}
std::chrono::high_resolution_clock::time_point endWork = std::chrono::high_resolution_clock::now();
++transactionCounter;
clockTicksUsed += std::chrono::duration_cast<std::chrono::milliseconds>(endWork - startWork).count();
current = std::chrono::high_resolution_clock::now();
}
std::lock_guard<std::mutex> tranMutex(m_totalTranMutex);
std::cout << "Thread " << threadID << " finished with " << transactionCounter << " transaction." << std::endl;
m_totalTrans += transactionCounter;
}
int main(int argc, char *argv[])
{
std::stringstream ss;
int numthreads = atoi(argv[1]);
std::list<std::thread> threads;
int threadIds = 1;
for(int i = 0; i < numthreads; ++i)
{
threads.push_back(std::thread(&WorkerThread, threadIds));
m_threadNativeHandles.insert(std::make_pair(threadIds, threads.rbegin()->native_handle()));
++threadIds;
}
{
std::lock_guard<std::mutex> lk(m_threadStartMutex);
m_startThreads = true;
}
m_allowThreadStart.notify_all();
//Join until completion
for(std::thread &th : threads)
{
th.join();
}
ss << "TotalTran" << std::endl
<< m_totalTrans << std::endl;
std::cout << ss.str();
}
Application usage: app N
where app is the name of the application and N is the number of threads to produce. The program runs for 1 minute.
On windows, I build this program with Visual Studio 2012. I execute the program on a quad core I7 (4 cores, 2 threads per core).
I get the following:
simplethread 1
Thread 1 finished with 1667 transaction.
TotalTran
1667
simplethread 2
Thread 1 finished with 1037 transaction.
Thread 2 finished with 1030 transaction.
TotalTran
2067
simplethread 3
Thread 3 finished with 824 transaction.
Thread 2 finished with 830 transaction.
Thread 1 finished with 837 transaction.
TotalTran
2491
simplethread 4
Thread 3 finished with 688 transaction.
Thread 2 finished with 693 transaction.
Thread 1 finished with 704 transaction.
Thread 4 finished with 691 transaction.
TotalTran
2776
simplethread 8
Thread 2 finished with 334 transaction.
Thread 6 finished with 325 transaction.
Thread 7 finished with 346 transaction.
Thread 1 finished with 329 transaction.
Thread 8 finished with 329 transaction.
Thread 3 finished with 338 transaction.
Thread 5 finished with 331 transaction.
Thread 4 finished with 330 transaction.
TotalTran
2662
E:\Development\Projects\Applications\CPUBenchmark\Debug>simplethread 16
Thread 16 finished with 163 transaction.
Thread 15 finished with 169 transaction.
Thread 12 finished with 165 transaction.
Thread 9 finished with 170 transaction.
Thread 10 finished with 166 transaction.
Thread 4 finished with 164 transaction.
Thread 13 finished with 166 transaction.
Thread 8 finished with 165 transaction.
Thread 6 finished with 165 transaction.
Thread 5 finished with 168 transaction.
Thread 2 finished with 161 transaction.
Thread 1 finished with 159 transaction.
Thread 7 finished with 160 transaction.
Thread 11 finished with 161 transaction.
Thread 14 finished with 163 transaction.
Thread 3 finished with 161 transaction.
TotalTran
2626
These numbers look a little poor. I was expecting a lot closer to double going from one thread doing X work to 2 threads doing 2X work on this system. The threads did do about the same amount of work but not as much in one minutes time.
It get's even stranger when I move to solaris.
On Solaris 11, using GCC 4.8.0, I build this program as follows:
gcc -o simple simpleThreads.cpp -I. -std=c++11 -DSOLARIS=1 -lstdc++ -lm
when i run "./simple 1", i get
Thread 1 finished with 19686 transaction.
TotalTran
19686
for "./simple 2", I get:
Thread 1 finished with 5248 transaction.
Thread 2 finished with 2484 transaction.
TotalTran
7732
On Solaris, the 2 thread case is much slower. I can't figure out what I'm doing wrong. I'm new to c++11 constructs and threads. So it's a double whammy. gcc -v shows the thread model is posix. Any help would be appreciated.
After googling around I found out that segmentation faults are given when the program is pointed to use memory that it doesn’t have access to.
I’ve recently started experiencing these errors after I tried making a custom button class that would enclose, in its clicked() signal, an integer.
Here’s the custom button class:
.h:
#include <QtGui>
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
class CustomButton : public QPushButton //This simple class allows us to map arguments in the Widget's cicked() signal.
{ //In the project, it's used in the delete-edit buttons so the program knows which part of the Movie/Theater/Screening list we're referring to
Q_OBJECT
public:
CustomButton(QString name,int num, QWidget *parent = 0);
signals:
void clicked(int pos);
private:
QSignalMapper *signalMapper;
};
#endif // CUSTOMBUTTON_H
.cpp:
#include "custombutton.h"
CustomButton::CustomButton(QString name,int num = 0, QWidget *parent) //Our constructor
: QPushButton(name,parent)
{ //Our button's now created and shown, through the superconstructor. Let's take care of its clicked() signal.
signalMapper = new QSignalMapper(this);
connect(this, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(this, num);
connect(signalMapper, SIGNAL(mapped(int)),this, SIGNAL(clicked(int)));
}
And then in my main code I do:
CustomButton *edit_button = new CustomButton("Edit",i,0);
edit_button->setFixedWidth(30);
connect(edit_button,SIGNAL(clicked(int)),this,SLOT(edit_movie(int))); //When the user decides to edit a movie, we set the class'es working_with var to the position of the film in the MovieList and we set the screen to the appropriate one.
CustomButton *del_button = new CustomButton("Delete",i,0); //We pass i as an argument to the button, so it can relate to the movie it's set next to.
del_button->setFixedWidth(45);
connect(del_button,SIGNAL(clicked(int)),this,SLOT(del_movie(int)));
Where i is the number I want it to have in the signal.
Thing is, thorugh the debugger it doesn’t give me a segfault if I press the Delete button. It all happens in the edit one.
As I’m still pretty confused and in a situation where I don’t even know WHAT to ask about, if you need anything else from me please say so and I will provide.
Here’s the backtrace, which I know not how to read:
0 QHash<QObject*, QString>::findNode qhash.h 884 0×69e98594
1 QHash<QObject*, QString>::contains qhash.h 874 0×69e98568
2 QSignalMapper::map qsignalmapper.cpp 267 0×69debe0c
3 QSignalMapper::map qsignalmapper.cpp 257 0×69debda2
4 QSignalMapper::qt_static_metacall moc_qsignalmapper.cpp 64 0×69debfce
5 QMetaObject::activate qobject.cpp 3547 0×69de9baf
6 QAbstractButton::clicked moc_qabstractbutton.cpp 220 0×10cb4b8
7 QAbstractButtonPrivate::emitClicked qabstractbutton.cpp 548 0xe2e517
8 QAbstractButtonPrivate::click qabstractbutton.cpp 541 0xe2e495
9 QAbstractButton::mouseReleaseEvent qabstractbutton.cpp 1123 0xe2f941
10 QWidget::event qwidget.cpp 8362 0xae63de
11 QAbstractButton::event qabstractbutton.cpp 1082 0xe2f7cc
12 QPushButton::event qpushbutton.cpp 683 0xecfeba
13 QApplicationPrivate::notify_helper qapplication.cpp 4554 0xa9c020
14 QApplication::notify qapplication.cpp 4097 0xa9a26a
15 QCoreApplication::notifyInternal qcoreapplication.cpp 876 0×69dd3b76
16 QCoreApplication::sendSpontaneousEvent qcoreapplication.h 234 0×113137e
17 QApplicationPrivate::sendMouseEvent qapplication.cpp 3163 0xa98ad6
18 QETWidget::translateMouseEvent qapplication_win.cpp 3363 0xb03171
19 QtWndProc qapplication_win.cpp 1696 0xafdf66
20 USER32!IsDialogMessageW C:\Windows\syswow64\user32.dll 0 0×76726238
21 USER32!RegisterSystemThread C:\Windows\syswow64\user32.dll 0 0×767278b0
22 ?? 0 0×30000
23 USER32!AllowForegroundActivation C:\Windows\syswow64\user32.dll 0 0×767268ea
24 qt_is_translatable_mouse_event qapplication_win.cpp 1463 0xafd465
25 USER32!GetMessageExtraInfo C:\Windows\syswow64\user32.dll 0 0×76727d31
26 ?? 0
Any heads up as to what might be causing the problem? As I said, the segfault only occurs if I run the program through the Debugger and when I press the "Edit" button, of the CustomButton class. When I normally build&run, the program works 9/10 times. Sometimes, clicking the edit button results in a crash. This erratic behaviour is what caused me to look for help here.
As when I click the button I'm taken to a new screen, I'm suspecting that the problem might lie in the decstructor? Does a blank destructor correctly deconstruct elements of objects of the CustomButton class, seeing as the actual object is passed to the base class constructor? Maybe I've got a leak there?
Unless knowing where you create the objects, it's hard to tell why there is a segfault in QSignalMapper. The pointer to your signal mapper might be invalid at some point in time, so there might be an unwanted deletion.
In some cases, a complete rebuild of your application might be helpful. Also re-running qmake. Just should be on your checklist when you can't explain why there is a segfault ...;)
There is an alternative to QSignalMapper: keep your signal clicked(int) and implement a private slot:
private slots:
this_clicked() {
emit clicked(num);
}
Then put in the constructor:
connect(this, SIGNAL(clicked()), SLOT(this_clicked()));