Event system for Qt source code analysis

Posted by Mikell on Fri, 17 Dec 2021 12:59:01 +0100

1, Add an event to the event loop

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

1.postEvent is to add an event event to the event queue of the thread to which the receiver object belongs and execute it in the next event cycle. It is asynchronous execution. After completion, postEvent() returns immediately. After addition, the ownership of the event belongs to the thread event queue. Therefore, event events should be allocated on the heap. Priority is the priority of events. Events with high priority will be executed first.

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

2.sendEvent() is a synchronous event dispatch. It does not enter the event queue, but directly enters the dispatch phase. It is executed in a thread. In other words, the thread where the sendEvent() interface is called and the thread represented by receiver - > thread() must be the same thread. After the event is sent, sendEvent is not responsible for deleting the event pointer. Therefore, when used, the event must be created on the stack. Here is an example I used in my project:

//QTreeWidget page up
void setTreeWidgetUpPage(QTreeView *tree)
{
    if(tree == nullptr)
        return;
    QKeyEvent pageUpEvent(QEvent::KeyPress,Qt::Key_PageUp,Qt::NoModifier);
    Q_ASSERT(QThread::currentThread() == tree->thread());//It must be satisfied, otherwise you need to use postEvent
    QApplication::sendEvent(tree,&pageUpEvent);
}

Take another example to experience the use environment of these two interfaces

//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);//sendEvent is used when the main thread calls quit
        QCoreApplication::sendEvent(q, &quitEvent);
    } else {//When other worker threads call quit, they need to execute asynchronously. Use postEvent. Note that new is used to allocate memory on the heap at this time
        QCoreApplication::postEvent(q, new QEvent(QEvent::Quit));
    }
}

2, Event loop in QThread

1. Borrow an example from another article Qtthread of Qt source code analysis As mentioned in the article, the virtual function [virtual protected] void QThread::run() is called in the real thread entry QThreadPrivate::start(), so let's start here.

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

2. In addition to using void QObject::moveToThread(QThread *targetThread) to implement multithreading described in the previous article, it can also be implemented by inheriting QThread class and rewriting QThread::run(). The code executed in run() runs in a new thread. There is no problem in implementation, but this usage is not recommended. The default implementation of run() function is very simple, The exec() function is called to open the thread's event loop.

//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();//Open thread event loop

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

3. The event loop will run until the exit() interface is called. It can be seen that the QEventLoop class is also used to open the event loop of the thread. QEventLoop is a derived class of QObject and also has thread affinity. According to the principle of which thread is created, the eventLoop instance also lives in the new thread.

//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();//The QThreadData thread data here is constructed in the constructor of QThreadPrivate,
	//All qobjects and their derived classes belonging to this thread hold this pointer, which reflects the thread affinity of 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;//Open the event loop and add 1 to the number of nested layers
            threadData->eventLoops.push(d->q_func());//This event loop is added to the event loop queue

            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();//Exit the queue after the event cycle exits
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --threadData->loopLevel;//Number of nesting layers minus one
        }
    };
    LoopReference ref(d, locker);//Handle queue in and queue out of event loops in constructors and destructors

    // 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())//As long as you do not exit, you will always wait and process the events in the event queue
        processEvents(flags | WaitForMoreEvents | EventLoopExec);

    ref.exceptionCaught = false;
    return d->returnCode.loadRelaxed();
}
  1. enum ProcessEventsFlags
  • QEventLoop::AllEvents: process all events
  • QEventLoop::ExcludeUserInputEvents: do not process user input events. Events will not be discarded and will be processed without this flag
  • QEventLoop::ExcludeSocketNotifiers: do not process network events
  • QEventLoop::WaitForMoreEvents: when the event queue is empty, continue to wait, and the thread does not sleep
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();
    if (!threadData->hasEventDispatcher())
        return false;
    return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}

Recall that there are several important members in QThreadData, which store the thread's event loop queue, event queue, thread pointer, thread ID and event distribution handle. Please post the code

class QThreadData
{
public:
    QThreadData(int initialRefCount = 1);
    ~QThreadData();
    ...
public:
    int loopLevel;//Number of event loop nesting levels
    int scopeLevel;

    QStack<QEventLoop *> eventLoops;//Nested event loop stack
    QPostEventList postEventList;//Thread event queue
    QAtomicPointer<QThread> thread;//Thread pointer
    QAtomicPointer<void> threadId;//Thread ID
    QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;//Event distribution list
    QList<void *> tls;
    FlaggedDebugSignatures flaggedSignatures;
    ...
};

QEventLoop::processEvents() further calls the interface of the QAbstractEventDispatcher class. This class is a virtual base class that defines the specific interface for event distribution. There are derivative classes that cannot be implemented on different platforms, so as to achieve cross platform purposes. Qeventdispatcher UNIX under Linux and qeventdispatcher Win32 under window. Let's take a look at the specific implementation under win, Let's look at the constructor first

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

    d->internalHwnd = qt_create_internal_window(this);
}

There is a very important point here. For window programming under win32, registering message callback functions can't get around, and Qt is no exception. In Qt, an invisible window is also created for each thread, and then the message callback function is registered for the window to take over the message loop of windows and translate the messages in Windows format into Qt's own message format, Enter Qt event system. D - > internalhwnd = Qt in constructor_ create_ internal_ window(this); Is the concrete realization,
Look at the code:

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,        // The geometry size is 0. This window is invisible
                            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;
}

Note that QWindowsMessageWindowClassContext ctx = qWindowsMessageWindowClassContext(); What is obtained here is a global static variable, which uses a macro Q defined in Qt_ GLOBAL_ Static, used to define a thread safe global static variable, Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext), qWindowsMessageWindowClassContext is actually an object, but it is overloaded with (), - >, and other operators. In behavior, it is expressed as a pointer to the object. Let's take a look at the constructor of this class.

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;//Here's the point. This is the message callback function of the above invisible window, which is a connection point between the windows message system and Qt
    wc.cbClsExtra = 0;					//It will not be expanded here for the time being and will be described in detail later
    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;
    }
}

In win32 window programming, when processing message loops, they are generally handled as follows:

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

In a while loop, keep getting messages from the windows message loop through PeekMessage, and then translate and distribute the messages. Finally, we registered the callback function qt_internal_proc() further processes messages. The processing of getting and distributing messages in Qt is in the processEvents() function. Let's go back here and have a look

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();//Retrieve user input message events
            } else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events
                msg = d->queuedSocketEvents.takeFirst();//Fetch network socket message event
            } else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {//PeekMessage is a win32 api used to retrieve messages from windows system message queues
                if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)//Get message
                    && 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) {//Processing timer events
                // 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;
            }
			//The filtering of events natively supported by each platform will return true if processed
            if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);//The win32 api enters the message distribution phase, and the event enters the message cycle of the windows system
            }
            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;
}

After the message is distributed, the previously registered message callback function Qt will be called through the windows message system_ internal_ Proc (), where windows system messages are translated into Qt messages, and the familiar events are programmed.

//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: {//Network socket events
        // 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 related Qt events
                    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://timer event 
        Q_ASSERT(d != nullptr);

        if (wp == d->sendPostedEventsTimerId)
            q->sendPostedEvents();
        else
            d->sendTimerEvent(wp);
        return 0;
    case WM_QT_SENDPOSTEDEVENTS://post events are handled here
        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();//Send the events in the event queue uniformly
        else
            d->startPostedEventsTimer();
        return 0;
    } // switch (message)

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

Void qeventdispatcher Win32:: sendpostedevents(), GUI related events are handled differently from other events here. QEvent::MetaCall such events related to slot function callback are handled through this interface, while window related events will enter void QWindowsGuiEventDispatcher::sendPostedEvents() in another overload function, which will be described in the event loop of the main thread, Here's the code:

//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());
}

You can see that the thread data pointer of this thread is sent out as a parameter here. The first parameter is 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, an internal class, is used to empty the event queue after sending out the events in the queue through sendEvent. As we said before, the event queue holds the ownership of the events allocated on the heap and is responsible for its memory release, qscopedpointer event_ Delete (E) by transferring the ownership of the event pointer to the smart pointer event_ On the delete, it is automatically released after exceeding the function of the smart pointer. Next, we will see the sendEvent we are familiar with. It will not be expanded here. We will open a single chapter later.

Topics: C++ Qt