После того как мы связали signal и слот через метод connect можно попробовать вызвать сигнал и посмотреть отладчиком как он работает.
Раннее мы примерно поняли как работает connect, см. connect изнутри.
Реализации метода сигнала в файле *.cpp нет, но он реализуется в moc_xxxxx.cpp файле, который нам автоматически генерирует moc препроцессор.
Вот пример одного из реализации сигналов:
// SIGNAL 0
void Class1::sig_foo2(const QVariant & _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
Внутри метода QMetaObject::activate собственно и будет вызываться слот:
QMetaObject::activate
// SIGNAL 0
void Class1::sig_foo2(const QVariant & _t1)
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
int signalOffset;
int methodOffset;
computeOffsets(m, &signalOffset, &methodOffset);
int signal_index = signalOffset + local_signal_index;
if (!sender->d_func()->isSignalConnected(signal_index))
return; // nothing connected to these signals, and no spy
if (sender->d_func()->blockSig)
return;
int signal_absolute_index = methodOffset + local_signal_index;
// формируем список аргументов для слота
void *empty_argv[] = { 0 };
if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,
argv ? argv : empty_argv);
}
// получили signal_index
Qt::HANDLE currentThreadId = QThread::currentThreadId();
QMutexLocker locker(signalSlotLock(sender));
QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
if (!connectionLists) {
locker.unlock();
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
return;
}
++connectionLists->inUse;
const QObjectPrivate::ConnectionList *list;
if (signal_index < connectionLists->count())
list = &connectionLists->at(signal_index);
else
list = &connectionLists->allsignals;
do {
QObjectPrivate::Connection *c = list->first;
if (!c) continue;
// We need to check against last here to ensure that signals added
// during the signal emission are not emitted in this emission.
QObjectPrivate::Connection *last = list->last;
do {
if (!c->receiver)
continue;
QObject * const receiver = c->receiver;
const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
continue;
#ifndef QT_NO_THREAD
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
locker.unlock();
if (receiverInSameThread) {
qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
"Sender is %s(%p), receiver is %s(%p)",
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
}
// если слот (ресивер) принадлежит другом потоку, то только в очередь ставим и не ждем завершения
QSemaphore semaphore;
QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
c->callFunction,
sender, signal_absolute_index,
0, 0,
argv ? argv : empty_argv,
&semaphore));
semaphore.acquire();
locker.relock();
continue;
#endif
}
QObjectPrivate::Sender currentSender;
QObjectPrivate::Sender *previousSender = 0;
if (receiverInSameThread) {
currentSender.sender = sender;
currentSender.signal = signal_absolute_index;
currentSender.ref = 1;
previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);
}
const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
const int method_relative = c->method_relative;
if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
//we compare the vtable to make sure we are not in the destructor of the object.
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0)
qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);
// это вызов в текущем потоке
callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());
locker.relock();
} else {
const int method = method_relative + c->method_offset;
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
qt_signal_spy_callback_set.slot_begin_callback(receiver,
method,
argv ? argv : empty_argv);
}
#if defined(QT_NO_EXCEPTIONS)
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
QT_TRY {
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
} QT_CATCH(...) {
locker.relock();
if (receiverInSameThread)
QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);
--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned && !connectionLists->inUse)
delete connectionLists;
QT_RETHROW;
}
#endif
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, method);
locker.relock();
}
if (receiverInSameThread)
QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);
if (connectionLists->orphaned)
break;
} while (c != last && (c = c->nextConnectionList) != 0);
if (connectionLists->orphaned)
break;
} while (list != &connectionLists->allsignals &&
//start over for all signals;
((list = &connectionLists->allsignals), true));
--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned) {
if (!connectionLists->inUse)
delete connectionLists;
} else if (connectionLists->dirty) {
sender->d_func()->cleanConnectionLists();
}
locker.unlock();
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
}
Работа с одним потоком
Для варианта когда sender и receiver в одном потоке будет вызываться метод callFunction:
сallFunction (для единого потока) это не что иное как вызов:
void Class1::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
Class1 *_t = static_cast<Class1 *>(_o);
switch (_id) {
case 0: _t->sig_foo1(); break;
case 1: _t->sig_foo2((*reinterpret_cast< const QVariant(*)>(_a[1]))); break;
case 2: _t->slot1(); break;
case 3: _t->slot_foo1(); break;
case 4: _t->slot_foo2((*reinterpret_cast< const QVariant(*)>(_a[1]))); break;
default: ;
}
}
}
То есть происходит банальный вызов функции с номером _id.
Когда sender и receiver принадлежат разным потокам.
Происходит постановка вызова в очередь сообщений потока через postEvent. И в этом случае мы уже не ждем завершения вызова слота.
Как оказывается все просто и логично. Это к тому, что в общем-то каждый может реализовать такой механизм типа сигнал/слот.