Qt源码解析之事件系统(一)

一、向事件循环里面添加事件

[static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)

1.postEvent是将event事件添加到接收者receiver对象所属线程的事件队列,并在下一个事件循环执行,为异步执行,完成后postEvent()立即返回,添加完成后,事件的所有权归线程事件队列所有,因此event事件要在堆上分配。priority为事件的优先级,优先级高的事件会优先执行。

[static] bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)

2.sendEvent()为同步事件派发,不入事件队列,直接进入派发环节,为线程内执行,换言之,调用sendEvent()接口所在线程和receiver->thread()所表示的线程,必须为同一个线程,事件发出后,sendEvent不负责event指针的删除,因此使用时,事件须在栈上创建。下面举一个我做项目时用到的一个例子:

//QTreeWidget向上翻页
void setTreeWidgetUpPage(QTreeView *tree)
{
    if(tree == nullptr)
        return;
    QKeyEvent pageUpEvent(QEvent::KeyPress,Qt::Key_PageUp,Qt::NoModifier);
    Q_ASSERT(QThread::currentThread() == tree->thread());//必然满足,否则就要使用postEvent
    QApplication::sendEvent(tree,&pageUpEvent);
}

再举一个例子,来体会一下这两个接口的使用环境

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qcoreapplication.cpp:2024
void QCoreApplicationPrivate::quit()
{
    Q_Q(QCoreApplication);

    if (QThread::currentThread() == mainThread()) {
        QEvent quitEvent(QEvent::Quit);//在主线程调用quit时,使用sendEvent
        QCoreApplication::sendEvent(q, &quitEvent);
    } else {//在其它工作线程调用quit时,就需要异步执行,使用postEvent,注意到此时使用new在堆上分配内存
        QCoreApplication::postEvent(q, new QEvent(QEvent::Quit));
    }
}

二、QThread里面的事件循环

1.借用另一篇文章里的例子Qt源码解析之QThread,文中提到,在真正的线程入口QThreadPrivate::start()里面调用了虚函数[virtual protected] void QThread::run(),我们就从这里开始。

void QThread::run()
{
    (void) exec();
}

2.除了之前文章中讲述的使用void QObject::moveToThread(QThread *targetThread)来实现多线程外,继承QThread类,然后重写QThread::run()同样也可以实现,在run()中执行的代码就是跑在新的线程中的,实现上么有问题,只是不推荐这种用法而已,run()函数的默认实现很简单,调用了exec()函数,以此开启了线程的事件循环。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread.cpp:543
int QThread::exec()
{
    Q_D(QThread);
    QMutexLocker locker(&d->mutex);
    d->data->quitNow = false;
    if (d->exited) {
        d->exited = false;
        return d->returnCode;
    }
    locker.unlock();

    QEventLoop eventLoop;
    int returnCode = eventLoop.exec();//开启线程事件循环

    locker.relock();
    d->exited = false;
    d->returnCode = -1;
    return returnCode;
}

3.事件循环会一直运行下去,直到exit()接口被调用,可以看到,开启线程的事件循环,同样使用了QEventLoop类,QEventLoop是QObject的派生类,同样具有线程亲和性,通过在哪个线程创建就属于哪个线程的原则,eventLoop实例同样活在新线程内。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventloop.cpp:181
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();//此处的QThreadData线程数据,就是在QThreadPrivate的构造函数中构造的,
	//所有隶属于该线程的QObject及其派生类都持有该指针,体现了QObject的线程亲和性。
    //we need to protect from race condition with QThread::exit
    QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);
    if (threadData->quitNow)
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }

    struct LoopReference {
        QEventLoopPrivate *d;
        QMutexLocker<QMutex> &locker;

        bool exceptionCaught;
        LoopReference(QEventLoopPrivate *d, QMutexLocker<QMutex> &locker) : d(d), locker(locker), exceptionCaught(true)
        {
            d->inExec = true;
            d->exit.storeRelease(false);

            auto threadData = d->threadData.loadRelaxed();
            ++threadData->loopLevel;//开启事件循环,嵌套层数加1
            threadData->eventLoops.push(d->q_func());//本事件循环加入事件循环队列

            locker.unlock();
        }

        ~LoopReference()
        {
            if (exceptionCaught) {
                qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                         "exceptions from an event handler is not supported in Qt.\n"
                         "You must not let any exception whatsoever propagate through Qt code.");
            }
            locker.relock();
            auto threadData = d->threadData.loadRelaxed();
            QEventLoop *eventLoop = threadData->eventLoops.pop();//事件循环退出后,出队
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --threadData->loopLevel;//嵌套层数减一
        }
    };
    LoopReference ref(d, locker);//在构造函数和析构函数中处理事件循环的入队、出队

    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);

#ifdef Q_OS_WASM
    // Partial support for nested event loops: Make the runtime throw a JavaSrcript
    // exception, which returns control to the browser while preserving the C++ stack.
    // Event processing then continues as normal. The sleep call below never returns.
    // QTBUG-70185
    if (threadData->loopLevel > 1)
        emscripten_sleep(1);
#endif

    while (!d->exit.loadAcquire())//只要没有退出,就一直等待并处理事件队列中的事件
        processEvents(flags | WaitForMoreEvents | EventLoopExec);

    ref.exceptionCaught = false;
    return d->returnCode.loadRelaxed();
}
  1. enum ProcessEventsFlags
  • QEventLoop::AllEvents:处理所有事件
  • QEventLoop::ExcludeUserInputEvents: 不处理用户输入事件,事件不会被丢弃,将会在没有此标志时被处理
  • QEventLoop::ExcludeSocketNotifiers: 不处理网络事件
  • QEventLoop::WaitForMoreEvents:在事件队列为空时,继续等待,线程不休眠
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();
    if (!threadData->hasEventDispatcher())
        return false;
    return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}

回忆一下,QThreadData里面有几个重要的成员,保存着线程的事件循环队列、事件队列、线程指针、线程ID以及事件分发句柄,在贴一下代码

class QThreadData
{
public:
    QThreadData(int initialRefCount = 1);
    ~QThreadData();
    ...
public:
    int loopLevel;//事件循环嵌套层数
    int scopeLevel;

    QStack<QEventLoop *> eventLoops;//嵌套事件循环堆栈
    QPostEventList postEventList;//线程事件队列
    QAtomicPointer<QThread> thread;//线程指针
    QAtomicPointer<void> threadId;//线程ID
    QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;//事件分发列表
    QList<void *> tls;
    FlaggedDebugSignatures flaggedSignatures;
    ...
};

QEventLoop::processEvents()进一步调用了QAbstractEventDispatcher类的接口,该类是一个虚基类,定义了事件分发的具体接口,在不通的平台上有不通的派生类实现,以此来达到跨平台的目的,Linux下为QEventDispatcherUNIX,window下为QEventDispatcherWin32,下面看一下win下具体实现,先看一下构造函数

QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent)
    : QAbstractEventDispatcher(dd, parent)
{
    Q_D(QEventDispatcherWin32);

    d->internalHwnd = qt_create_internal_window(this);
}

这里有个非常重要的地方,win32下的窗口编程,注册消息回调函数是绕不过去的,Qt也不例外,在Qt中同样是为每一个线程创建了一个看不见的窗口,然后通过为该窗口注册消息回调函数,以此来接管windows的消息循环,将windows格式的消息,翻译成Qt自己的消息格式,进入到Qt事件系统。构造函数中的 d->internalHwnd = qt_create_internal_window(this);就是具体的实现了,
看一下代码:

static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
    QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();
    if (!ctx->atom)
        return 0;
    HWND wnd = CreateWindow(ctx->className,    // classname
                            ctx->className,    // window name
                            0,                 // style
                            0, 0, 0, 0,        // geometry  尺寸为0,这个窗口隐身啦
                            HWND_MESSAGE,            // parent
                            0,                 // menu handle
                            GetModuleHandle(0),     // application
                            0);                // windows creation data.

    if (!wnd) {
        qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
        return 0;
    }

#ifdef GWLP_USERDATA
    SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
#else
    SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
#endif

    return wnd;
}

注意到QWindowsMessageWindowClassContext ctx = qWindowsMessageWindowClassContext();此处获取的是一个全局静态变量,使用了Qt内定义的一个宏Q_GLOBAL_STATIC,用来定义一个线程安全的全局静态变量,Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext),qWindowsMessageWindowClassContext实际上是一个对象,只不过被重载了()、->、 等操作符,在行为上表现为该对象的指针我们来看一下这个类的构造函数。

QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext()
    : atom(0), className(0)
{
    // make sure that multiple Qt's can coexist in the same process
    const QString qClassName = QStringLiteral("QEventDispatcherWin32_Internal_Widget")
        + QString::number(quintptr(qt_internal_proc));
    className = new wchar_t[qClassName.size() + 1];
    qClassName.toWCharArray(className);
    className[qClassName.size()] = 0;

    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = qt_internal_proc;//重点来了,这就是上述隐形窗口的消息回调函数,算是windows消息系统与Qt的一个衔接点
    wc.cbClsExtra = 0;					//此处暂时不再展开,后续会有详细说明
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(0);
    wc.hIcon = 0;
    wc.hCursor = 0;
    wc.hbrBackground = 0;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;
    atom = RegisterClass(&wc);
    if (!atom) {
        qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName));
        delete [] className;
        className = 0;
    }
}

win32窗口编程中,在处理消息循环时,一般有类似如下处理:

MSG msg;
while(!isExit()){
	if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){
		 TranslateMessage(&msg);
         DispatchMessage(&msg);
	}
}

在一个while循环中不停的通过PeekMessage从windows消息循环中获取消息然后翻译、分发消息,最终我们之前注册的回调函数qt_internal_proc(),对消息做进一步的处理,Qt中获取并分发消息的处理正是在processEvents()函数里,下面我们回归这里看看

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    // We don't know _when_ the interrupt occurred so we have to honor it.
    const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
    emit awake();

    // To prevent livelocks, send posted events once per iteration.
    // QCoreApplication::sendPostedEvents() takes care about recursions.
    sendPostedEvents();

    if (wasInterrupted)
        return false;

    auto threadData = d->threadData.loadRelaxed();
    bool canWait;
    bool retVal = false;
    do {
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt.loadRelaxed()) {
            MSG msg;

            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // process queued user input events
                msg = d->queuedUserInputEvents.takeFirst();//取出用户输入消息事件
            } else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events
                msg = d->queuedSocketEvents.takeFirst();//取出网络socket消息事件
            } else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {//PeekMessage是win32 api,用于从windows系统消息队列中
                if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)//获取消息
                    && isUserInputMessage(msg.message)) {
                    // queue user input events for later processing
                    d->queuedUserInputEvents.append(msg);
                    continue;
                }
                if ((flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                    // queue socket events for later processing
                    d->queuedSocketEvents.append(msg);
                    continue;
                }
            } else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE)
                       == WAIT_OBJECT_0) {
                // a new message has arrived, process it
                continue;
            } else {
                // nothing to do, so break
                break;
            }

            if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
                d->startPostedEventsTimer();
                // Set result to 'true' because the message was sent by wakeUp().
                retVal = true;
                continue;
            }
            if (msg.message == WM_TIMER) {//处理定时器事件
                // Skip timer event intended for use inside foreign loop.
                if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
                    continue;

                // avoid live-lock by keeping track of the timers we've already sent
                bool found = false;
                for (int i = 0; !found && i < processedTimers.count(); ++i) {
                    const MSG processed = processedTimers.constData()[i];
                    found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                }
                if (found)
                    continue;
                processedTimers.append(msg);
            } else if (msg.message == WM_QUIT) {
                if (QCoreApplication::instance())
                    QCoreApplication::instance()->quit();
                return false;
            }
			//各平台原生支持的事件的过滤,如果被处理了,就会返回true
            if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);//win32 api 进入消息分发环节,此时事件进入到windows系统的消息循环了
            }
            retVal = true;
        }

        // wait for message
        canWait = (!retVal
                   && !d->interrupt.loadRelaxed()
                   && flags.testFlag(QEventLoop::WaitForMoreEvents)
                   && threadData->canWaitLocked());
        if (canWait) {
            emit aboutToBlock();
            MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
            emit awake();
        }
    } while (canWait);

    return retVal;
}

消息分发后,经windows消息系统,就会调用之前注册的消息回调函数qt_internal_proc(),在这里将windows系统消息翻译成Qt消息,编程我们熟悉的事件的样子。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:241
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
    if (message == WM_NCCREATE)
        return true;

    MSG msg;
    msg.hwnd = hwnd;
    msg.message = message;
    msg.wParam = wp;
    msg.lParam = lp;
    QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
    qintptr result;
    if (!dispatcher) {
        if (message == WM_TIMER)
            KillTimer(hwnd, wp);
        return 0;
    }
    if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result))
        return result;

#ifdef GWLP_USERDATA
    auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
#else
    auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA));
#endif
    QEventDispatcherWin32Private *d = nullptr;
    if (q != nullptr)
        d = q->d_func();

    switch (message) {
    case WM_QT_SOCKETNOTIFIER: {//网络socket事件
        // socket notifier message
        int type = -1;
        switch (WSAGETSELECTEVENT(lp)) {
        case FD_READ:
        case FD_ACCEPT:
            type = 0;
            break;
        case FD_WRITE:
        case FD_CONNECT:
            type = 1;
            break;
        case FD_OOB:
            type = 2;
            break;
        case FD_CLOSE:
            type = 3;
            break;
        }
        if (type >= 0) {
            Q_ASSERT(d != nullptr);
            QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
            QSNDict *dict = sn_vec[type];

            QSockNot *sn = dict ? dict->value(wp) : 0;
            if (sn == nullptr) {
                d->postActivateSocketNotifiers();
            } else {
                Q_ASSERT(d->active_fd.contains(sn->fd));
                QSockFd &sd = d->active_fd[sn->fd];
                if (sd.selected) {
                    Q_ASSERT(sd.mask == 0);
                    d->doWsaAsyncSelect(sn->fd, 0);
                    sd.selected = false;
                }
                d->postActivateSocketNotifiers();

                // Ignore the message if a notification with the same type was
                // received previously. Suppressed message is definitely spurious.
                const long eventCode = WSAGETSELECTEVENT(lp);
                if ((sd.mask & eventCode) != eventCode) {
                    sd.mask |= eventCode;
                    QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);//socket 相关Qt事件
                    QCoreApplication::sendEvent(sn->obj, &event);
                }
            }
        }
        return 0;
    }
    case WM_QT_ACTIVATENOTIFIERS: {
        Q_ASSERT(d != nullptr);

        // Postpone activation if we have unhandled socket notifier messages
        // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of
        // event processing.
        MSG msg;
        if (!PeekMessage(&msg, d->internalHwnd,
                         WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)
            && d->queuedSocketEvents.isEmpty()) {
            // register all socket notifiers
            for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();
                 it != end; ++it) {
                QSockFd &sd = it.value();
                if (!sd.selected) {
                    d->doWsaAsyncSelect(it.key(), sd.event);
                    // allow any event to be accepted
                    sd.mask = 0;
                    sd.selected = true;
                }
            }
        }
        d->activateNotifiersPosted = false;
        return 0;
    }
    case WM_TIMER://定时器事件
        Q_ASSERT(d != nullptr);

        if (wp == d->sendPostedEventsTimerId)
            q->sendPostedEvents();
        else
            d->sendTimerEvent(wp);
        return 0;
    case WM_QT_SENDPOSTEDEVENTS://post的事件在此处处理
        Q_ASSERT(d != nullptr);

        // We send posted events manually, if the window procedure was invoked
        // by the foreign event loop (e.g. from the native modal dialog).
        // Skip sending, if the message queue is not empty.
        // sendPostedEventsTimer will deliver posted events later.
        static const UINT mask = QS_ALLEVENTS;
        if (HIWORD(GetQueueStatus(mask)) == 0)
            q->sendPostedEvents();//将事件队列中的事件统一发送出去
        else
            d->startPostedEventsTimer();
        return 0;
    } // switch (message)

    return DefWindowProc(hwnd, message, wp, lp);
}

void QEventDispatcherWin32::sendPostedEvents(),gui相关的事件和其余事件在此处处理不一样,QEvent::MetaCall这样跟槽函数回调有关的事件,通过此接口处理,而窗口相关的事件,会进入另一个重载函数中void QWindowsGuiEventDispatcher::sendPostedEvents(),这个会在主线程事件循环中讲到,下面看一下代码:

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:892
void QEventDispatcherWin32::sendPostedEvents()
{
    Q_D(QEventDispatcherWin32);

    if (d->sendPostedEventsTimerId != 0)
        KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
    d->sendPostedEventsTimerId = 0;

    // Allow posting WM_QT_SENDPOSTEDEVENTS message.
    d->wakeUps.storeRelaxed(0);

    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}

可以看到,此处将本线程的线程数据指针作为参数传出,第一个参数为receiver=0,event_type=0

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qcoreapplication.cpp:1838
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
    if (event_type == -1) {
        // we were called by an obsolete event dispatcher.
        event_type = 0;
    }

    if (receiver && receiver->d_func()->threadData != data) {
        qWarning("QCoreApplication::sendPostedEvents: Cannot send "
                 "posted events for objects in another thread");
        return;
    }

    ++data->postEventList.recursion;

    auto locker = qt_unique_lock(data->postEventList.mutex);

    // by default, we assume that the event dispatcher can go to sleep after
    // processing all events. if any new events are posted while we send
    // events, canWait will be set to false.
    data->canWait = (data->postEventList.size() == 0);

    if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) {
        --data->postEventList.recursion;
        return;
    }

    data->canWait = true;

    // okay. here is the tricky loop. be careful about optimizing
    // this, it looks the way it does for good reasons.
    int startOffset = data->postEventList.startOffset;
    int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;
    data->postEventList.insertionOffset = data->postEventList.size();

    // Exception-safe cleaning up without the need for a try/catch block
    struct CleanUp {
        QObject *receiver;
        int event_type;
        QThreadData *data;
        bool exceptionCaught;

        inline CleanUp(QObject *receiver, int event_type, QThreadData *data) :
            receiver(receiver), event_type(event_type), data(data), exceptionCaught(true)
        {}
        inline ~CleanUp()
        {
            if (exceptionCaught) {
                // since we were interrupted, we need another pass to make sure we clean everything up
                data->canWait = false;
            }

            --data->postEventList.recursion;
            if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())
                data->eventDispatcher.loadRelaxed()->wakeUp();

            // clear the global list, i.e. remove everything that was
            // delivered.
            if (!event_type && !receiver && data->postEventList.startOffset >= 0) {
                const QPostEventList::iterator it = data->postEventList.begin();
                data->postEventList.erase(it, it + data->postEventList.startOffset);
                data->postEventList.insertionOffset -= data->postEventList.startOffset;
                Q_ASSERT(data->postEventList.insertionOffset >= 0);
                data->postEventList.startOffset = 0;
            }
        }
    };
    CleanUp cleanup(receiver, event_type, data);

    while (i < data->postEventList.size()) {
        // avoid live-lock
        if (i >= data->postEventList.insertionOffset)
            break;

        const QPostEvent &pe = data->postEventList.at(i);
        ++i;

        if (!pe.event)
            continue;
        if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) {
            data->canWait = false;
            continue;
        }

        if (pe.event->type() == QEvent::DeferredDelete) {
            // DeferredDelete events are sent either
            // 1) when the event loop that posted the event has returned; or
            // 2) if explicitly requested (with QEvent::DeferredDelete) for
            //    events posted by the current event loop; or
            // 3) if the event was posted before the outermost event loop.

            int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
            int loopLevel = data->loopLevel + data->scopeLevel;
            const bool allowDeferredDelete =
                (eventLevel > loopLevel
                 || (!eventLevel && loopLevel > 0)
                 || (event_type == QEvent::DeferredDelete
                     && eventLevel == loopLevel));
            if (!allowDeferredDelete) {
                // cannot send deferred delete
                if (!event_type && !receiver) {
                    // we must copy it first; we want to re-post the event
                    // with the event pointer intact, but we can't delay
                    // nulling the event ptr until after re-posting, as
                    // addEvent may invalidate pe.
                    QPostEvent pe_copy = pe;

                    // null out the event so if sendPostedEvents recurses, it
                    // will ignore this one, as it's been re-posted.
                    const_cast<QPostEvent &>(pe).event = nullptr;

                    // re-post the copied event so it isn't lost
                    data->postEventList.addEvent(pe_copy);
                }
                continue;
            }
        }

        // first, we diddle the event so that we can deliver
        // it, and that no one will try to touch it later.
        pe.event->m_posted = false;
        QEvent *e = pe.event;
        QObject * r = pe.receiver;

        --r->d_func()->postedEvents;
        Q_ASSERT(r->d_func()->postedEvents >= 0);

        // next, update the data structure so that we're ready
        // for the next event.
        const_cast<QPostEvent &>(pe).event = nullptr;

        locker.unlock();
        const auto relocker = qScopeGuard([&locker] { locker.lock(); });

        QScopedPointer<QEvent> event_deleter(e); // will delete the event (with the mutex unlocked)

        // after all that work, it's time to deliver the event.
        QCoreApplication::sendEvent(r, e);

        // careful when adding anything below this point - the
        // sendEvent() call might invalidate any invariants this
        // function depends on.
    }

    cleanup.exceptionCaught = false;
}

CleanUp这个内部类,用来在将队列中的事件通过sendEvent派发出去后,将事件队列清空,之前我们说过,事件队列持有在堆上分配的事件的所有权,负责它的内存释放,QScopedPointer event_deleter(e),通过将事件指针的所有权转移到智能指针event_deleter上,在超出智能指针的作用后,自动释放掉。接下来就看到我们熟悉的sendEvent了,这里就不展开啦,后续再开单章。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值