// Copyright (C) 2018 The Qt Company Ltd. // Copyright (C) 2020 UBports Foundataion. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include "mockcompositor.h" using namespace MockCompositor; /* * This class simulate a thread from another library which use poll() to wait * for data from Wayland compositor. */ class ExternalWaylandReaderThread : public QThread { public: ExternalWaylandReaderThread(struct wl_display *disp) : QThread() , m_disp(disp) { setObjectName(QStringLiteral("ExternalWaylandReader")); } ~ExternalWaylandReaderThread() { if (m_pipefd[1] != -1 && write(m_pipefd[1], "q", 1) == -1) qWarning("Failed to write to the pipe: %s.", strerror(errno)); wait(); } protected: void run() override { // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. struct Pipe { Pipe(int *fds) : fds(fds) { if (::pipe(fds) != 0) qWarning("Pipe creation failed. Quitting may hang."); } ~Pipe() { if (fds[0] != -1) { close(fds[0]); close(fds[1]); } } int *fds; } pipe(m_pipefd); struct wl_event_queue *a_queue = wl_display_create_queue(m_disp); struct pollfd fds[2] = { { wl_display_get_fd(m_disp), POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } }; while (true) { // No wl_proxy is assigned to this queue, thus guaranteed to be always empty. Q_ASSERT(wl_display_prepare_read_queue(m_disp, a_queue) == 0); wl_display_flush(m_disp); // Wakeup every 10 seconds so that if Qt blocks in _read_events(), // it won't last forever. poll(fds, /* nfds */ 2, 10000); if (fds[0].revents & POLLIN) { wl_display_read_events(m_disp); } else { wl_display_cancel_read(m_disp); } if (fds[1].revents & POLLIN) { char pipeIn; if (read(m_pipefd[0], &pipeIn, 1) == 1 && pipeIn == 'q') break; } } wl_event_queue_destroy(a_queue); } private: struct wl_display *m_disp; int m_pipefd[2] = { -1, -1 }; }; class tst_multithreaded : public QObject, private DefaultCompositor { Q_OBJECT private slots: void initTestCase() { m_config.autoConfigure = true; m_config.autoEnter = false; } void init() { // a test case is given new simulated thread. QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); struct wl_display *wl_dpy = (struct wl_display *)native->nativeResourceForWindow("display", NULL); m_extThread.reset(new ExternalWaylandReaderThread(wl_dpy)); m_extThread->start(); } void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } void mainThreadIsNotBlocked(); public: QScopedPointer m_extThread; }; void tst_multithreaded::mainThreadIsNotBlocked() { QElapsedTimer timer; timer.start(); QTest::qWait(100); QVERIFY(timer.elapsed() < 200); } QCOMPOSITOR_TEST_MAIN(tst_multithreaded) #include "tst_multithreaded.moc"