summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Anderson <Sven.Anderson@snom.com>2011-09-08 17:40:55 +0200
committerQt by Nokia <qt-info@nokia.com>2011-11-15 10:16:12 +0100
commit2b7d98ef8fbd6cf49326fa0bbf154e9bacbb7b49 (patch)
tree250f2752acb679a237504ea70888b0ad5efa1251
parent51b7d3c8b621c2de6f98f465f478ec574bb14195 (diff)
Allow to create a custom event dispatcher for specific QThreads.
QAbstractEventDispatcher() does no longer install itself into the current thread. Instead the new methods QThread::setEventDispatcher() and QCoreApplication::setEventDispatcher() allow to install a custom event dispatcher into any QThread as long as there is no default event dispatcher created yet. That is, before the thread has been started with QThread::start() or, in case of the main thread, before QCoreApplication has been instantiated. Change-Id: I7367e13d8d8aebed5a5651260bb69b8818eb1b90 Reviewed-by: Olivier Goffart <ogoffart@woboq.com> Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp29
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher_p.h1
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp29
-rw-r--r--src/corelib/kernel/qcoreapplication.h3
-rw-r--r--src/corelib/thread/qthread.cpp32
-rw-r--r--src/corelib/thread/qthread.h4
-rw-r--r--src/corelib/thread/qthread_unix.cpp6
-rw-r--r--src/corelib/thread/qthread_win.cpp7
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp55
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp74
10 files changed, 211 insertions, 29 deletions
diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp
index 10de2caf62..b936ac4ed2 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.cpp
+++ b/src/corelib/kernel/qabstracteventdispatcher.cpp
@@ -49,16 +49,6 @@
QT_BEGIN_NAMESPACE
-void QAbstractEventDispatcherPrivate::init()
-{
- Q_Q(QAbstractEventDispatcher);
- if (threadData->eventDispatcher != 0) {
- qWarning("QAbstractEventDispatcher: An event dispatcher has already been created for this thread");
- } else {
- threadData->eventDispatcher = q;
- }
-}
-
// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers
struct QtTimerIdFreeListConstants : public QFreeListDefaultConstants
{
@@ -127,8 +117,9 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
instance() and call functions on the QAbstractEventDispatcher
object that is returned. If you want to use your own instance of
QAbstractEventDispatcher or of a QAbstractEventDispatcher
- subclass, you must create your instance \e before you create the
- QApplication object.
+ subclass, you must install it with QCoreApplication::setEventDispatcher()
+ or QThread::setEventDispatcher() \e before a default event dispatcher has
+ been installed.
The main event loop is started by calling
QCoreApplication::exec(), and stopped by calling
@@ -145,29 +136,21 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
reimplementation of QAbstractEventDispatcher that merges Qt and
Motif events together.
- \sa QEventLoop, QCoreApplication
+ \sa QEventLoop, QCoreApplication, QThread
*/
/*!
Constructs a new event dispatcher with the given \a parent.
*/
QAbstractEventDispatcher::QAbstractEventDispatcher(QObject *parent)
- : QObject(*new QAbstractEventDispatcherPrivate, parent)
-{
- Q_D(QAbstractEventDispatcher);
- d->init();
-}
+ : QObject(*new QAbstractEventDispatcherPrivate, parent) {}
/*!
\internal
*/
QAbstractEventDispatcher::QAbstractEventDispatcher(QAbstractEventDispatcherPrivate &dd,
QObject *parent)
- : QObject(dd, parent)
-{
- Q_D(QAbstractEventDispatcher);
- d->init();
-}
+ : QObject(dd, parent) {}
/*!
Destroys the event dispatcher.
diff --git a/src/corelib/kernel/qabstracteventdispatcher_p.h b/src/corelib/kernel/qabstracteventdispatcher_p.h
index 620a4499e2..31c579d865 100644
--- a/src/corelib/kernel/qabstracteventdispatcher_p.h
+++ b/src/corelib/kernel/qabstracteventdispatcher_p.h
@@ -67,7 +67,6 @@ public:
inline QAbstractEventDispatcherPrivate()
: event_filter(0)
{ }
- void init();
QAbstractEventDispatcher::EventFilter event_filter;
static int allocateTimerId();
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 9408d414f0..c30f6135a9 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -660,8 +660,10 @@ void QCoreApplication::init()
d->createEventDispatcher();
Q_ASSERT(QCoreApplicationPrivate::eventDispatcher != 0);
- if (!QCoreApplicationPrivate::eventDispatcher->parent())
+ if (!QCoreApplicationPrivate::eventDispatcher->parent()) {
QCoreApplicationPrivate::eventDispatcher->moveToThread(d->threadData->thread);
+ QCoreApplicationPrivate::eventDispatcher->setParent(this);
+ }
d->threadData->eventDispatcher = QCoreApplicationPrivate::eventDispatcher;
@@ -2517,6 +2519,31 @@ bool QCoreApplication::hasPendingEvents()
return false;
}
+/*!
+ Returns a pointer to the event dispatcher object for the main thread. If no
+ event dispatcher exists for the thread, this function returns 0.
+*/
+QAbstractEventDispatcher *QCoreApplication::eventDispatcher()
+{
+ if (QCoreApplicationPrivate::theMainThread)
+ return QCoreApplicationPrivate::theMainThread->eventDispatcher();
+ return 0;
+}
+
+/*!
+ Sets the event dispatcher for the main thread to \a eventDispatcher. This
+ is only possible as long as there is no event dispatcher installed yet. That
+ is, before QCoreApplication has been instantiated. This method takes
+ ownership of the object.
+*/
+void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
+{
+ QThread *mainThread = QCoreApplicationPrivate::theMainThread;
+ if (!mainThread)
+ mainThread = QThread::currentThread(); // will also setup theMainThread
+ mainThread->setEventDispatcher(eventDispatcher);
+}
+
/*
\fn void QCoreApplication::watchUnixSignal(int signal, bool watch)
\internal
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index ce3f349844..4d3ee9bc7a 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -61,6 +61,7 @@ class QTextCodec;
class QTranslator;
class QPostEventList;
class QStringList;
+class QAbstractEventDispatcher;
#define qApp QCoreApplication::instance()
@@ -114,6 +115,8 @@ public:
static void removePostedEvents(QObject *receiver);
static void removePostedEvents(QObject *receiver, int eventType);
static bool hasPendingEvents();
+ static QAbstractEventDispatcher *eventDispatcher();
+ static void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
virtual bool notify(QObject *, QEvent *);
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index 6cc8a4875a..17b0f2cb94 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -757,4 +757,36 @@ QThread::QThread(QThreadPrivate &dd, QObject *parent)
#endif // QT_NO_THREAD
+/*!
+ Returns a pointer to the event dispatcher object for the thread. If no event
+ dispatcher exists for the thread, this function returns 0.
+*/
+QAbstractEventDispatcher *QThread::eventDispatcher() const
+{
+ Q_D(const QThread);
+ return d->data->eventDispatcher;
+}
+
+/*!
+ Sets the event dispatcher for the thread to \a eventDispatcher. This is
+ only possible as long as there is no event dispatcher installed for the
+ thread yet. That is, before the thread has been started with start() or, in
+ case of the main thread, before QCoreApplication has been instantiated.
+ This method takes ownership of the object.
+*/
+void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
+{
+ Q_D(QThread);
+ if (d->data->eventDispatcher != 0) {
+ qWarning("QThread::setEventDispatcher: An event dispatcher has already been created for this thread");
+ } else {
+ eventDispatcher->moveToThread(this);
+ if (eventDispatcher->thread() == this) // was the move successful?
+ d->data->eventDispatcher = eventDispatcher;
+ else
+ qWarning("QThread::setEventDispatcher: Could not move event dispatcher to target thread");
+ }
+}
+
+
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h
index 89a3ad8b1f..baad793969 100644
--- a/src/corelib/thread/qthread.h
+++ b/src/corelib/thread/qthread.h
@@ -54,6 +54,7 @@ QT_MODULE(Core)
class QThreadData;
class QThreadPrivate;
+class QAbstractEventDispatcher;
#ifndef QT_NO_THREAD
class Q_CORE_EXPORT QThread : public QObject
@@ -92,6 +93,9 @@ public:
void exit(int retcode = 0);
+ QAbstractEventDispatcher *eventDispatcher() const;
+ void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
+
public Q_SLOTS:
void start(Priority = InheritPriority);
void terminate();
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
index 46bc641d16..8c6fefcfd7 100644
--- a/src/corelib/thread/qthread_unix.cpp
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -289,8 +289,10 @@ void *QThreadPrivate::start(void *arg)
data->quitNow = thr->d_func()->exited;
}
- // ### TODO: allow the user to create a custom event dispatcher
- createEventDispatcher(data);
+ if (data->eventDispatcher) // custom event dispatcher set?
+ data->eventDispatcher->startingUp();
+ else
+ createEventDispatcher(data);
emit thr->started();
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index 80d7f3ff63..5b74bad7ef 100644
--- a/src/corelib/thread/qthread_win.cpp
+++ b/src/corelib/thread/qthread_win.cpp
@@ -315,8 +315,11 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
QMutexLocker locker(&thr->d_func()->mutex);
data->quitNow = thr->d_func()->exited;
}
- // ### TODO: allow the user to create a custom event dispatcher
- createEventDispatcher(data);
+
+ if (data->eventDispatcher) // custom event dispatcher set?
+ data->eventDispatcher->startingUp();
+ else
+ createEventDispatcher(data);
#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINCE)
// sets the name of the current thread.
diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
index 62275f841a..09f31414a6 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
+++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
@@ -62,6 +62,7 @@ private slots:
void reexec();
void execAfterExit();
void eventLoopExecAfterExit();
+ void customEventDispatcher();
};
class EventSpy : public QObject
@@ -586,5 +587,59 @@ void tst_QCoreApplication::eventLoopExecAfterExit()
QCOMPARE(loop.exec(), 0);
}
+class DummyEventDispatcher : public QAbstractEventDispatcher {
+public:
+ DummyEventDispatcher() : QAbstractEventDispatcher(), visited(false) {}
+ bool processEvents(QEventLoop::ProcessEventsFlags) {
+ visited = true;
+ emit awake();
+ QCoreApplication::sendPostedEvents();
+ return false;
+ }
+ bool hasPendingEvents() {
+ extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
+ return qGlobalPostedEventsCount();
+ }
+ void registerSocketNotifier(QSocketNotifier *) {}
+ void unregisterSocketNotifier(QSocketNotifier *) {}
+ void registerTimer(int , int , QObject *) {}
+ bool unregisterTimer(int ) { return false; }
+ bool unregisterTimers(QObject *) { return false; }
+ QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); }
+ void wakeUp() {}
+ void interrupt() {}
+ void flush() {}
+
+ bool visited;
+};
+
+void tst_QCoreApplication::customEventDispatcher()
+{
+ // there should be no ED yet
+ QVERIFY(!QCoreApplication::eventDispatcher());
+ DummyEventDispatcher *ed = new DummyEventDispatcher;
+ QCoreApplication::setEventDispatcher(ed);
+ // the new ED should be set
+ QCOMPARE(QCoreApplication::eventDispatcher(), ed);
+ // test the alternative API of QAbstractEventDispatcher
+ QCOMPARE(QAbstractEventDispatcher::instance(), ed);
+ QWeakPointer<DummyEventDispatcher> weak_ed(ed);
+ QVERIFY(!weak_ed.isNull());
+ {
+ int argc = 1;
+ char *arg0 = "tst_qcoreapplication";
+ char *argv[] = { arg0 };
+ QCoreApplication app(argc, argv);
+ // instantiating app should not overwrite the ED
+ QCOMPARE(QCoreApplication::eventDispatcher(), ed);
+ QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
+ app.exec();
+ // the custom ED has really been used?
+ QVERIFY(ed->visited);
+ }
+ // ED has been deleted?
+ QVERIFY(weak_ed.isNull());
+}
+
QTEST_APPLESS_MAIN(tst_QCoreApplication)
#include "tst_qcoreapplication.moc"
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index 6d7a02b3cc..fcf993bb7b 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -107,6 +107,8 @@ private slots:
void startAndQuitCustomEventLoop();
void isRunningInFinished();
+ void customEventDispatcher();
+
#ifndef Q_OS_WINCE
void stressTest();
#endif
@@ -1218,5 +1220,77 @@ void tst_QThread::isRunningInFinished()
}
}
+class DummyEventDispatcher : public QAbstractEventDispatcher {
+public:
+ DummyEventDispatcher() : QAbstractEventDispatcher(), visited(false) {}
+ bool processEvents(QEventLoop::ProcessEventsFlags) {
+ visited = true;
+ emit awake();
+ QCoreApplication::sendPostedEvents();
+ return false;
+ }
+ bool hasPendingEvents() {
+ extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
+ return qGlobalPostedEventsCount();
+ }
+ void registerSocketNotifier(QSocketNotifier *) {}
+ void unregisterSocketNotifier(QSocketNotifier *) {}
+ void registerTimer(int , int , QObject *) {}
+ bool unregisterTimer(int ) { return false; }
+ bool unregisterTimers(QObject *) { return false; }
+ QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); }
+ void wakeUp() {}
+ void interrupt() {}
+ void flush() {}
+
+ bool visited;
+};
+
+class ThreadObj : public QObject
+{
+ Q_OBJECT
+public slots:
+ void visit() {
+ emit visited();
+ }
+signals:
+ void visited();
+};
+
+void tst_QThread::customEventDispatcher()
+{
+ QThread thr;
+ // there should be no ED yet
+ QVERIFY(!thr.eventDispatcher());
+ DummyEventDispatcher *ed = new DummyEventDispatcher;
+ thr.setEventDispatcher(ed);
+ // the new ED should be set
+ QCOMPARE(thr.eventDispatcher(), ed);
+ // test the alternative API of QAbstractEventDispatcher
+ QCOMPARE(QAbstractEventDispatcher::instance(&thr), ed);
+ thr.start();
+ // start() should not overwrite the ED
+ QCOMPARE(thr.eventDispatcher(), ed);
+
+ ThreadObj obj;
+ obj.moveToThread(&thr);
+ // move was successful?
+ QCOMPARE(obj.thread(), &thr);
+ QEventLoop loop;
+ connect(&obj, SIGNAL(visited()), &loop, SLOT(quit()), Qt::QueuedConnection);
+ QMetaObject::invokeMethod(&obj, "visit", Qt::QueuedConnection);
+ loop.exec();
+ // test that the ED has really been used
+ QVERIFY(ed->visited);
+
+ QWeakPointer<DummyEventDispatcher> weak_ed(ed);
+ QVERIFY(!weak_ed.isNull());
+ thr.quit();
+ // wait for thread to be stopped
+ QVERIFY(thr.wait(30000));
+ // test that ED has been deleted
+ QVERIFY(weak_ed.isNull());
+}
+
QTEST_MAIN(tst_QThread)
#include "tst_qthread.moc"