From 78125ea2f051107b84fdc0354acdedb7885308ee Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Thu, 6 Dec 2012 16:47:27 -0600 Subject: Add real threading support, including per-thread event loops, to QThread --- src/kernel/qeventloop_x11.cpp | 98 ++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 48 deletions(-) (limited to 'src/kernel/qeventloop_x11.cpp') diff --git a/src/kernel/qeventloop_x11.cpp b/src/kernel/qeventloop_x11.cpp index 833be69..cd3e865 100644 --- a/src/kernel/qeventloop_x11.cpp +++ b/src/kernel/qeventloop_x11.cpp @@ -146,55 +146,57 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) if ( qt_is_gui_used ) { QApplication::sendPostedEvents(); - // Two loops so that posted events accumulate - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - // also flushes output buffer - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - if ( d->shortcut ) { - return FALSE; - } - - XNextEvent( QPaintDevice::x11AppDisplay(), &event ); - - if ( flags & ExcludeUserInput ) { - switch ( event.type ) { - case ButtonPress: - case ButtonRelease: - case MotionNotify: - case XKeyPress: - case XKeyRelease: - case EnterNotify: - case LeaveNotify: - continue; - - case ClientMessage: - { - // from qapplication_x11.cpp - extern Atom qt_wm_protocols; - extern Atom qt_wm_take_focus; - extern Atom qt_qt_scrolldone; - - // only keep the wm_take_focus and - // qt_qt_scrolldone protocols, discard all - // other client messages - if ( event.xclient.format != 32 ) - continue; - - if ( event.xclient.message_type == qt_wm_protocols || - (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) - break; - if ( event.xclient.message_type == qt_qt_scrolldone ) - break; + if (QApplication::isGuiThread()) { + // Two loops so that posted events accumulate + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + // also flushes output buffer + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + if ( d->shortcut ) { + return FALSE; + } + + XNextEvent( QPaintDevice::x11AppDisplay(), &event ); + + if ( flags & ExcludeUserInput ) { + switch ( event.type ) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case XKeyPress: + case XKeyRelease: + case EnterNotify: + case LeaveNotify: + continue; + + case ClientMessage: + { + // from qapplication_x11.cpp + extern Atom qt_wm_protocols; + extern Atom qt_wm_take_focus; + extern Atom qt_qt_scrolldone; + + // only keep the wm_take_focus and + // qt_qt_scrolldone protocols, discard all + // other client messages + if ( event.xclient.format != 32 ) + continue; + + if ( event.xclient.message_type == qt_wm_protocols || + (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) + break; + if ( event.xclient.message_type == qt_qt_scrolldone ) + break; + } + + default: break; + } + } + + nevents++; + if ( qApp->x11ProcessEvent( &event ) == 1 ) + return TRUE; } - - default: break; - } } - - nevents++; - if ( qApp->x11ProcessEvent( &event ) == 1 ) - return TRUE; - } } } @@ -261,7 +263,7 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) FD_ZERO( &d->sn_vec[2].select_fds ); } - if ( qt_is_gui_used ) { + if ( qt_is_gui_used && QApplication::isGuiThread() ) { // select for events on the event socket - only on X11 FD_SET( d->xfd, &d->sn_vec[0].select_fds ); highest = QMAX( highest, d->xfd ); -- cgit v1.2.3 From caf80d88243aaa00e8f1baeaa6b7e4c3aca75f63 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Thu, 6 Dec 2012 18:29:37 -0600 Subject: Add threading tutorial and fix a couple rare crashes --- src/kernel/qapplication.cpp | 9 ++- src/kernel/qeventloop_unix.cpp | 2 + src/kernel/qeventloop_x11.cpp | 6 +- tutorial/t15/main.cpp | 173 +++++++++++++++++++++++++++++++++++++++++ tutorial/t15/main.h | 45 +++++++++++ tutorial/t15/t15.pro | 5 ++ tutorial/tutorial.pro | 2 +- 7 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 tutorial/t15/main.cpp create mode 100644 tutorial/t15/main.h create mode 100644 tutorial/t15/t15.pro (limited to 'src/kernel/qeventloop_x11.cpp') diff --git a/src/kernel/qapplication.cpp b/src/kernel/qapplication.cpp index a9c9fb8..f3b0119 100644 --- a/src/kernel/qapplication.cpp +++ b/src/kernel/qapplication.cpp @@ -2767,7 +2767,10 @@ bool QApplication::internalNotify( QObject *receiver, QEvent * e) if (!handled) { #if defined(QT_THREAD_SUPPORT) - bool locked = QApplication::qt_mutex->locked(); + bool locked = false; + if (QApplication::qt_mutex) { + locked = QApplication::qt_mutex->locked(); + } if (locked) { QApplication::qt_mutex->unlock(); } @@ -2775,7 +2778,9 @@ bool QApplication::internalNotify( QObject *receiver, QEvent * e) consumed = receiver->event( e ); #if defined(QT_THREAD_SUPPORT) if (locked) { - QApplication::qt_mutex->lock(); + if (QApplication::qt_mutex) { + QApplication::qt_mutex->lock(); + } } #endif } diff --git a/src/kernel/qeventloop_unix.cpp b/src/kernel/qeventloop_unix.cpp index 202ef12..80c8f29 100644 --- a/src/kernel/qeventloop_unix.cpp +++ b/src/kernel/qeventloop_unix.cpp @@ -562,6 +562,8 @@ int QEventLoop::activateTimers() n_act++; QTimerEvent e( t->id ); QApplication::sendEvent( t->obj, &e ); // send event + if ( !timerList ) // sendEvent allows other threads to execute, therefore we must check for list existence when it returns! + return 0; if ( timerList->findRef( begin ) == -1 ) begin = 0; } diff --git a/src/kernel/qeventloop_x11.cpp b/src/kernel/qeventloop_x11.cpp index cd3e865..5ee41ca 100644 --- a/src/kernel/qeventloop_x11.cpp +++ b/src/kernel/qeventloop_x11.cpp @@ -284,7 +284,8 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) // unlock the GUI mutex and select. when we return from this function, there is // something for us to do #if defined(QT_THREAD_SUPPORT) - locker.mutex()->unlock(); + if ( locker.mutex() ) locker.mutex()->unlock(); + else return false; #endif int nsel; @@ -298,7 +299,8 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) // relock the GUI mutex before processing any pending events #if defined(QT_THREAD_SUPPORT) - locker.mutex()->lock(); + if ( locker.mutex() ) locker.mutex()->lock(); + else return false; #endif // we are awake, broadcast it diff --git a/tutorial/t15/main.cpp b/tutorial/t15/main.cpp new file mode 100644 index 0000000..4c96083 --- /dev/null +++ b/tutorial/t15/main.cpp @@ -0,0 +1,173 @@ +/**************************************************************** +** +** Qt threading tutorial +** (c) 2012 Timothy Pearson +** +** This tutorial is released into the Public Domain and +** can therefore be modified and/or used for any purpose +** +****************************************************************/ + +#include "main.h" + +#include + +#include +#include + +void WorkerObject::run() +{ + qDebug( "[%s] thread: %p event loop: %p", threadFriendlyName.ascii(), QThread::currentThreadObject(), QApplication::eventLoop() ); + + QEventLoop* eventLoop = QApplication::eventLoop(); + if (!eventLoop) return; + + QTimer *t = new QTimer(this); + connect( t, SIGNAL(timeout()), SLOT(timerHandler()) ); + t->start( 1000, FALSE ); + + for( int count = 0; count < 5; count++ ) { + sleep( 1 ); + qDebug( "[%s] Ping!", threadFriendlyName.ascii() ); + displayMessage("Hi", "There!"); + eventLoop->processEvents(QEventLoop::AllEvents); + } + + eventLoop->exit(0); +} + +void WorkerObject::timerHandler() +{ + qDebug( "[%s] Timer fired!", threadFriendlyName.ascii() ); +} + +void MainObject::emitMessage(QString str1, QString str2) +{ + qDebug( "%s", ("[MainObject] emitMessage: " + str1 + " " + str2).ascii() ); +} + +void MainObject::buttonClicked() +{ + qDebug( "[MainObject] Button clicked!" ); + + QEventLoop* eventLoop = QApplication::eventLoop(); + if (!eventLoop) return; + eventLoop->exit(0); +} + +#define SET_UP_WORKER(x, y, z) \ + WorkerObject x; \ + x.threadFriendlyName = y; \ + x.moveToThread(&z); \ + QObject::connect(&x, SIGNAL(displayMessage(QString,QString)), &mainobject, SLOT(emitMessage(QString,QString))); \ + QTimer::singleShot(0, &x, SLOT(run())); + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + qDebug( "[MainObject] thread: %p event loop: %p", QThread::currentThreadObject(), QApplication::eventLoop() ); + + QPushButton hello( "Exit", 0 ); + hello.resize( 100, 30 ); + + MainObject mainobject; + + QEventLoopThread workerthread0; + QEventLoopThread workerthread1; + QEventLoopThread workerthread2; + QEventLoopThread workerthread3; + QEventLoopThread workerthread4; + QEventLoopThread workerthread5; + QEventLoopThread workerthread6; + QEventLoopThread workerthread7; + QEventLoopThread workerthread8; + QEventLoopThread workerthread9; + + QEventLoopThread workerthread10; + QEventLoopThread workerthread11; + QEventLoopThread workerthread12; + QEventLoopThread workerthread13; + QEventLoopThread workerthread14; + QEventLoopThread workerthread15; + QEventLoopThread workerthread16; + QEventLoopThread workerthread17; + QEventLoopThread workerthread18; + QEventLoopThread workerthread19; + + SET_UP_WORKER(workerobject0, "WorkerObject0", workerthread0) + SET_UP_WORKER(workerobject1, "WorkerObject1", workerthread1) + SET_UP_WORKER(workerobject2, "WorkerObject2", workerthread2) + SET_UP_WORKER(workerobject3, "WorkerObject3", workerthread3) + SET_UP_WORKER(workerobject4, "WorkerObject4", workerthread4) + SET_UP_WORKER(workerobject5, "WorkerObject5", workerthread5) + SET_UP_WORKER(workerobject6, "WorkerObject6", workerthread6) + SET_UP_WORKER(workerobject7, "WorkerObject7", workerthread7) + SET_UP_WORKER(workerobject8, "WorkerObject8", workerthread8) + SET_UP_WORKER(workerobject9, "WorkerObject9", workerthread9) + + SET_UP_WORKER(workerobject10, "WorkerObjec10", workerthread10) + SET_UP_WORKER(workerobject11, "WorkerObjec11", workerthread11) + SET_UP_WORKER(workerobject12, "WorkerObjec12", workerthread12) + SET_UP_WORKER(workerobject13, "WorkerObjec13", workerthread13) + SET_UP_WORKER(workerobject14, "WorkerObjec14", workerthread14) + SET_UP_WORKER(workerobject15, "WorkerObjec15", workerthread15) + SET_UP_WORKER(workerobject16, "WorkerObjec16", workerthread16) + SET_UP_WORKER(workerobject17, "WorkerObjec17", workerthread17) + SET_UP_WORKER(workerobject18, "WorkerObjec18", workerthread18) + SET_UP_WORKER(workerobject19, "WorkerObjec19", workerthread19) + + workerthread0.start(); + workerthread1.start(); + workerthread2.start(); + workerthread3.start(); + workerthread4.start(); + workerthread5.start(); + workerthread6.start(); + workerthread7.start(); + workerthread8.start(); + workerthread9.start(); + + workerthread10.start(); + workerthread11.start(); + workerthread12.start(); + workerthread13.start(); + workerthread14.start(); + workerthread15.start(); + workerthread16.start(); + workerthread17.start(); + workerthread18.start(); + workerthread19.start(); + + a.setMainWidget( &hello ); + QObject::connect(&hello, SIGNAL(clicked()), &mainobject, SLOT(buttonClicked())); + hello.show(); + a.exec(); + hello.hide(); + + qDebug( "[MainObject] Waiting for thread completion..." ); + + workerthread0.wait(); + workerthread1.wait(); + workerthread2.wait(); + workerthread3.wait(); + workerthread4.wait(); + workerthread5.wait(); + workerthread6.wait(); + workerthread7.wait(); + workerthread8.wait(); + workerthread9.wait(); + + workerthread10.wait(); + workerthread11.wait(); + workerthread12.wait(); + workerthread13.wait(); + workerthread14.wait(); + workerthread15.wait(); + workerthread16.wait(); + workerthread17.wait(); + workerthread18.wait(); + workerthread19.wait(); + + qDebug( "[MainObject] Finished!" ); +} diff --git a/tutorial/t15/main.h b/tutorial/t15/main.h new file mode 100644 index 0000000..13ac5df --- /dev/null +++ b/tutorial/t15/main.h @@ -0,0 +1,45 @@ +/**************************************************************** +** +** Qt threading tutorial +** (c) 2012 Timothy Pearson +** +** This tutorial is released into the Public Domain and +** can therefore be modified and/or used for any purpose +** +****************************************************************/ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include +#include +#include +#include + +class MainObject; + +class WorkerObject : public QObject +{ + Q_OBJECT + + public slots: + void run(); + void timerHandler(); + + signals: + void displayMessage(QString, QString); + + public: + QString threadFriendlyName; +}; + +class MainObject : public QObject +{ + Q_OBJECT + + public slots: + void emitMessage(QString, QString); + void buttonClicked(); +}; + +#endif // _MAIN_H_ diff --git a/tutorial/t15/t15.pro b/tutorial/t15/t15.pro new file mode 100644 index 0000000..ff0f08d --- /dev/null +++ b/tutorial/t15/t15.pro @@ -0,0 +1,5 @@ +TEMPLATE = app +CONFIG += qt warn_on release +HEADERS = main.h +SOURCES = main.cpp +TARGET = t15 diff --git a/tutorial/tutorial.pro b/tutorial/tutorial.pro index 32b31d7..90e37f7 100644 --- a/tutorial/tutorial.pro +++ b/tutorial/tutorial.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS = t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14 +SUBDIRS = t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14 t15 -- cgit v1.2.3