Qt 信号槽连接源码解读

日常打盹 / 2023-08-24 / 原文

在声明信号的时候,Qt 会将信号于 QMetaThod 进行绑定,当emit 一个信号的时候, 会调用一个 QMetaMethod::invoke 函数。

查看源码, 在qmetaobject.h 中看到一个函数:

bool QMetaMethod::invoke(QObject *object,
Qt::ConnectionType connectionType,
QGenericReturnArgument returnValue,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) const

{
...

  QObjectPrivate::StaticMetaCallFunction callFunction = mobj->d.static_metacall;

  if (connectionType == Qt::DirectConnection) {
    if (callFunction) {
      callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
      return true;
    } else {
      return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0;
    }
  }

  else if (connectionType == Qt::QueuedConnection) {
    if (returnValue.data()) {
      qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
      "queued connections");
      return false;
    }

    int nargs = 1; // include return type
    void **args = (void **) malloc(paramCount * sizeof(void *));
    Q_CHECK_PTR(args);
    int *types = (int *) malloc(paramCount * sizeof(int));
    Q_CHECK_PTR(types);
    types[0] = 0; // return type
    args[0] = 0;

    for (int i = 1; i < paramCount; ++i) {
      types[i] = QMetaType::type(typeNames[i]);
      if (types[i] == QMetaType::UnknownType && param[i]) {
        // Try to register the type and try again before reporting an error.
        int index = nargs - 1;
        void *argv[] = { &types[i], &index };
        QMetaObject::metacall(object, QMetaObject::RegisterMethodArgumentMetaType,
        idx_relative + idx_offset, argv);
        if (types[i] == -1) {
          qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
          typeNames[i]);
          for (int x = 1; x < i; ++x) {
            if (types[x] && args[x])
            QMetaType::destroy(types[x], args[x]);
          }
          free(types);
          free(args);
          return false;
        }
      }
      if (types[i] != QMetaType::UnknownType) {
        args[i] = QMetaType::create(types[i], param[i]);
        ++nargs;
      }
    }

    QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,0, -1, nargs, types, args));
  }

  else { // blocking queued connection
#if QT_CONFIG(thread)
    if (currentThread == objectThread) {
      qWarning("QMetaMethod::invoke: Dead lock detected in "
      "BlockingQueuedConnection: Receiver is %s(%p)", mobj->className(), object);
    }

    QSemaphore semaphore;
    QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,0, -1, 0, 0, param, &semaphore));
    semaphore.acquire();
#endif // QT_CONFIG(thread)
  }

}

 

可以看到当信号槽是Qt::DirectConnection的时候,会直接调用槽函数,
是Qt::QueuedConnection 连接的话,就 压入事件队列 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,0, -1, nargs, types, args));

而在 下列函数中实现事件的压缩。

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority) {

...

//对事件压缩

QMutexUnlocker locker(&data->postEventList.mutex);

// if this is one of the compressible events, do compression
if (receiver->d_func()->postedEvents
&& self && self->compressEvent(event, receiver, &data->postEventList)) {
Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);
return;
}

if (event->type() == QEvent::DeferredDelete)
receiver->d_ptr->deleteLaterCalled = true;

if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
// remember the current running eventloop for DeferredDelete
// events posted in the receiver's thread.

// Events sent by non-Qt event handlers (such as glib) may not
// have the scopeLevel set correctly. The scope level makes sure that
// code like this:
// foo->deleteLater();
// qApp->processEvents(); // without passing QEvent::DeferredDelete
// will not cause "foo" to be deleted before returning to the event loop.

// If the scope level is 0 while loopLevel != 0, we are called from a
// non-conformant code path, and our best guess is that the scope level
// should be 1. (Loop level 0 is special: it means that no event loops
// are running.)
int loopLevel = data->loopLevel;
int scopeLevel = data->scopeLevel;
if (scopeLevel == 0 && loopLevel != 0)
scopeLevel = 1;
static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
}

// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
QScopedPointer<QEvent> eventDeleter(event);
Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
data->postEventList.addEvent(QPostEvent(receiver, event, priority));  //事件加入队列
eventDeleter.take();
event->posted = true;
++receiver->d_func()->postedEvents;
data->canWait = false;
locker.unlock();

QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
if (dispatcher)
dispatcher->wakeUp();  //唤醒事件循环


}

...
}

 

而在 事件分发器的 

void QEventDispatcherUNIX::wakeUp()
{
  Q_D(QEventDispatcherUNIX);
  d->threadPipe.wakeUp();  //唤醒主线程
}

//唤醒QT 事件

void QThreadPipe::wakeUp()
{
  if (wakeUps.testAndSetAcquire(0, 1)) {
#ifndef QT_NO_EVENTFD
    if (fds[1] == -1) {
      // eventfd
      eventfd_t value = 1;
      int ret;
      EINTR_LOOP(ret, eventfd_write(fds[0], value));
      return;
    }
#endif
    char c = 0;
    qt_safe_write(fds[1], &c, 1);  //使用管道
  }
}

 

在 事件循环中调用事件: