Funky Qt emit/activate bug? Now with MORE information!
-
I have some code. This code, in my Win32 release build only, as built on the production build machine only (i.e. when I build on my local machine, the bug does not exhibit), exhibits a funky bug.
This call exists:
@emit pathChanged(path);@
where path is a QString. In the debugger (which took an age to get sorted, needing to alter the build machine to add debug symbols and make pdb files and all that) at this point I can see it has a nice sensible value.This is the next level down in the stack, in a moc_ file (autogenerated)
@void pathContext::pathChanged(const QString & _t1)const
{
void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(const_cast< pathContext *>(this), &staticMetaObject, 0, _a);
}@At this point, as you can see, _a is an array of void pointers, the first one being just a null void pointer, and the second being a pointer-to-QString recast as a void pointer.
A couple of levels in the stack later (I cannot see into the intermediate levels), we arrive here:
@void Workplace::MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
MainWindow *_t = static_cast<MainWindow *>(_o);
switch (_id) {
case 0: _t->mainWindowStarted(); break;
case 1: _t->setPath((reinterpret_cast< const QString()>(_a[1]))); break;@That setPath is the slot that I connected the signal to. All looks good so far, but there's a problem. Here it crashes. When I look at the value of _a, which is recognised as a void * * in the debugger, it's 0x00000001.
So it seems to me that the dereference there is case_1 is trying to dereference a pointer to memory location 0x00000001, which predictably causes problems.
What makes no sense, though, is that:
-
This is autogenerated code. It leaves me code at the @emit pathChanged(path);@ and path is just a simple QString, so how does something go wrong in the very few following steps.
-
The first time through this code, everything is fine. It only goes wrong the SECOND time through. Identical input (i.e. same value for path).
-
Doesn't exhibit in debug versions, or 64 bit versions, or Linux or Solaris versions, or any version except the 32 bit Win release version as built on the build machine.
I can't come up with any sensible ideas for what would cause such an odd Heisenbug (although Heisenbug no more now that I can reproduce it, if only by hijacking the production build machine).
Does anyone have any ideas? I'm trying to see what the value of _a is at that point when it doesn't crash, but this debug setup is all held together with string at the moment, just to see this.
-
-
Addendum: When I very naughtily break it at the case_1 statement, and check the value of a_ and find it to be 0×00000001, if i manually change that value to match the value of _a in the activate statement in the previous code, it works; so I'm comnig around to the conclusion that something in QT is trashing that value, but only the SECOND time through this signal/slot sequence.
This makes no sense to me :(
-
This is quite deep inside of your code. It will be hard for anyone to make the right suggestion.
However, while through your post a question popped up. Did perform a rerun of qmake and a complete rebuild on the machine with the problem?
Out of personal experience, this helps quite often. -
I've dug a lot deeper (and had the Win32 QT libs rebuilt, in release, but with debugging symbols added). Maybe I have enough now that someone can shed some light on this and help me out.
Way deep in qobject.cpp is this function:
@void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv)@
That argv has a non-zero value. Ultimately, when it's working, many lines later, is this call (in a do-while loop):
@callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);@
Clearly, if argv is non-zero (i.e. we have a parameter), than the intent is to get that parameter to the function we're going to call. So far, so good. Sometimes, however, that argv value (which was a sensible value at the start of the function QMetaObject::activate) has become 0x00000001 at this point.
I have found the line of code that changes argv from a sensible value to this bad value; still inside QMetaObject::activate, way down at line 3557 in the version of the code I'm using (about 140 lines into the function in the version of the code I'm using)
@if (receiverInSameThread)
QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);@This changes the value of argv (or at least, when I put a breakpoint before this line, argv is good, and when I have a breakpoint after this line, argv is bad), and when the do-while loop repeats, disaster.
I've got no idea what this code is all about. I have recently spotted that the slot being called throws an exception; the cycle from a higher level is signal calls slot, slot throws exception, everything carries on, user repeats exactly the same, signal calls slot, and then this argv being changed issue leading to a segFault. I recall that calling exceptions (disclaimer; I'm the maintainer and new developer, not the original writer, so I'm not 100% sure what's happening anywhere in this code) from within slots is potentially problematic. Could that be related?
-
Hi,
AFAIK, throwing exceptions from a slot is not supported and IIRC you also need to have a build of Qt with exceptions enabled to use them (BUT that doesn't mean it's getting supported in slots). There's the "Exception Safety" chapter in the documentation about that.
I would recommend you (if possible) to get rid of exceptions. You can also search the development mailing list archive, there was a discussion about exception recently