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>
This commit is contained in:
Sven Anderson 2011-09-08 17:40:55 +02:00 committed by Qt by Nokia
parent 51b7d3c8b6
commit 2b7d98ef8f
10 changed files with 211 additions and 29 deletions

View File

@ -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.

View File

@ -67,7 +67,6 @@ public:
inline QAbstractEventDispatcherPrivate()
: event_filter(0)
{ }
void init();
QAbstractEventDispatcher::EventFilter event_filter;
static int allocateTimerId();

View File

@ -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

View File

@ -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 *);

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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.

View File

@ -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"

View File

@ -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"