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:
parent
51b7d3c8b6
commit
2b7d98ef8f
@ -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.
|
||||
|
@ -67,7 +67,6 @@ public:
|
||||
inline QAbstractEventDispatcherPrivate()
|
||||
: event_filter(0)
|
||||
{ }
|
||||
void init();
|
||||
QAbstractEventDispatcher::EventFilter event_filter;
|
||||
|
||||
static int allocateTimerId();
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -289,7 +289,9 @@ void *QThreadPrivate::start(void *arg)
|
||||
data->quitNow = thr->d_func()->exited;
|
||||
}
|
||||
|
||||
// ### TODO: allow the user to create a custom event dispatcher
|
||||
if (data->eventDispatcher) // custom event dispatcher set?
|
||||
data->eventDispatcher->startingUp();
|
||||
else
|
||||
createEventDispatcher(data);
|
||||
|
||||
emit thr->started();
|
||||
|
@ -315,7 +315,10 @@ 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
|
||||
|
||||
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)
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user