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/qobject.cpp | 328 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 285 insertions(+), 43 deletions(-) (limited to 'src/kernel/qobject.cpp') diff --git a/src/kernel/qobject.cpp b/src/kernel/qobject.cpp index 7e01dec..13486f4 100644 --- a/src/kernel/qobject.cpp +++ b/src/kernel/qobject.cpp @@ -50,22 +50,107 @@ #include "qptrvector.h" #ifdef QT_THREAD_SUPPORT -#include +#include "qmutex.h" #include +#include "qthread.h" #endif #include - +#include #ifndef QT_NO_USERDATA class QObjectPrivate : public QPtrVector +#else +class QObjectPrivate { +#endif { public: +#ifndef QT_NO_USERDATA QObjectPrivate( uint s ) : QPtrVector(s){ setAutoDelete( TRUE ); } +#endif + QThread* ownThread; }; -#else -class QObjectPrivate { + +#if defined(QT_THREAD_SUPPORT) + +void QObject::moveToThread_helper(QThread *targetThread) +{ + QEvent e(QEvent::ThreadChange); + QApplication::sendEvent(this, &e); + + if (childObjects) { + QObject *child; + QObjectListIt it(*childObjects); + while ( (child=it.current()) ) { + ++it; + child->moveToThread_helper(targetThread); + } + } +} + +void QObject::setThreadObject_helper(QThread *targetThread) +{ + d->ownThread = targetThread; + + if (childObjects) { + QObject *child; + QObjectListIt it(*childObjects); + while ( (child=it.current()) ) { + ++it; + child->moveToThread_helper(targetThread); + } + } +} + +/*! + Changes the thread affinity for this object and its children. The + object cannot be moved if it has a parent. Event processing will + continue in the \a targetThread. To move an object to the main + thread, pass QApplication::guiThread() as the \a targetThread. + + Note that all active timers for the object will be reset. The + timers are first stopped in the current thread and restarted (with + the same interval) in the \a targetThread. As a result, constantly + moving an object between threads can postpone timer events + indefinitely. + + \sa contextThreadObject() + */ +void QObject::moveToThread(QThread *targetThread) +{ + QMutexLocker locker( QApplication::qt_mutex ); + + if (parentObj) { +#if defined(QT_DEBUG) + qWarning( "QObject::moveToThread: Cannot move objects with a parent" ); +#endif + return; + } + if (isWidget) { +#if defined(QT_DEBUG) + qWarning( "QObject::moveToThread: Widgets cannot be moved to a new thread" ); +#endif + return; + } + + QThread *objectThread = contextThreadObject(); + QThread *currentThread = QThread::currentThreadObject(); + + if (objectThread != currentThread) { +#if defined(QT_DEBUG) + qWarning( "QObject::moveToThread: Current thread is not the object's thread" ); +#endif + return; + } + + if (objectThread == targetThread) { + return; + } + + moveToThread_helper(targetThread); + setThreadObject_helper(targetThread); } + #endif class QSenderObjectList : public QObjectList, public QShared @@ -75,6 +160,41 @@ public: QObject *currentSender; }; +class Q_EXPORT QMetaCallEvent : public QEvent +{ +public: + enum MetaCallType { + MetaCallEmit = 0, + MetaCallInvoke = 1 + }; + +public: + QMetaCallEvent(int id, QObject *sender, QUObject *data, MetaCallType type); + ~QMetaCallEvent(); + + inline int id() const { return id_; } + inline QObject *sender() const { return sender_; } + inline QUObject *data() const { return data_; } + inline MetaCallType type() const { return type_; } + +private: + const int id_; + QObject *sender_; + QUObject *data_; + const MetaCallType type_; +}; + +/*! \internal + */ +QMetaCallEvent::QMetaCallEvent(int id, QObject *sender, QUObject *data, MetaCallType type) + :QEvent(MetaCall), id_(id), sender_(sender), data_(data), type_(type) +{ } + +/*! \internal + */ +QMetaCallEvent::~QMetaCallEvent() +{ } + /*! \class Qt qnamespace.h @@ -269,7 +389,21 @@ void *qt_find_obj_child( QObject *parent, const char *type, const char *name ) return 0; } +#ifdef QT_THREAD_SUPPORT +/*! + Returns a pointer to the QThread* associated with + the current thread affinity of this object. + + \sa moveToThread() + */ + +QThread* QObject::contextThreadObject() const +{ + return d->ownThread; +} + +#endif #ifndef QT_NO_PRELIMINARY_SIGNAL_SPY /* @@ -436,6 +570,11 @@ QObject::QObject( QObject *parent, const char *name ) insert_tree( this ); isTree = TRUE; } + + if ( !d ) + d = new QObjectPrivate(0); + + d->ownThread = QThread::currentThreadObject(); } @@ -720,6 +859,36 @@ QObject* QObject::child( const char *objName, const char *inheritsClass, return obj; } +/*! \internal */ +QUObject* deepCopyQUObjectArray(QUObject* origArray) +{ + QUObject* newArray; + int count = 0; + while (!((origArray+count)->isLastObject)) { + count++; + } + count++; + newArray = (QUObject*)malloc(sizeof(QUObject)*count); + for (int i=0; ideepCopy(newArray+i); + } + return newArray; +} + +/*! \internal */ +void destroyDeepCopiedQUObjectArray(QUObject* uArray) +{ + int count = 0; + while (!((uArray+count)->isLastObject)) { + count++; + } + count++; + for (int i=0; i~QUObject(); + } + free(uArray); +} + /*! \fn bool QObject::isWidgetType() const @@ -777,6 +946,40 @@ bool QObject::event( QEvent *e ) delete this; return TRUE; + case QEvent::MetaCall: + { + QMetaCallEvent* metaEvent = dynamic_cast(e); + if (metaEvent) { + if (d->ownThread == QThread::currentThreadObject()) { + QSenderObjectList* sol; + QObject* oldSender = 0; + sol = senderObjects; + if ( sol ) { + oldSender = sol->currentSender; + sol->ref(); + sol->currentSender = metaEvent->sender(); + } + QUObject *o = metaEvent->data(); + if (metaEvent->type() == QMetaCallEvent::MetaCallEmit) { + qt_emit( metaEvent->id(), o ); + } + if (metaEvent->type() == QMetaCallEvent::MetaCallInvoke) { + qt_invoke( metaEvent->id(), o ); + } + if (sol ) { + sol->currentSender = oldSender; + if ( sol->deref() ) { + delete sol; + } + } + } + else { + qWarning("QObject: Ignoring metacall event from non-owning thread"); + } + destroyDeepCopiedQUObjectArray(metaEvent->data()); + } + } + default: if ( e->type() >= QEvent::User ) { customEvent( (QCustomEvent*) e ); @@ -2337,6 +2540,7 @@ void QObject::activate_signal( int signal ) if ( !signalsBlocked() && signal >= 0 && ( !connections || !connections->at( signal ) ) ) { QUObject o[1]; + o[0].isLastObject = true; qt_spy_signal( this, signal, o ); return; } @@ -2349,6 +2553,7 @@ void QObject::activate_signal( int signal ) if ( !clist ) return; QUObject o[1]; + o[0].isLastObject = true; activate_signal( clist, o ); } @@ -2364,6 +2569,8 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o ) qt_spy_signal( this, connections->findRef( clist), o ); #endif + const QThread *currentThread = QThread::currentThreadObject(); + QObject *object; QSenderObjectList* sol; QObject* oldSender = 0; @@ -2377,10 +2584,26 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o ) sol->ref(); sol->currentSender = this; } - if ( c->memberType() == QSIGNAL_CODE ) - object->qt_emit( c->member(), o ); - else - object->qt_invoke( c->member(), o ); + if ( c->memberType() == QSIGNAL_CODE ) { + if (object->d->ownThread == currentThread) { + object->qt_emit( c->member(), o ); + } + else { + if (object->d->ownThread && !object->d->ownThread->finished()) { + QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallEmit)); + } + } + } + else { + if (object->d->ownThread == currentThread) { + object->qt_invoke( c->member(), o ); + } + else { + if (object->d->ownThread && !object->d->ownThread->finished()) { + QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallInvoke)); + } + } + } if ( sol ) { sol->currentSender = oldSender; if ( sol->deref() ) @@ -2401,10 +2624,26 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o ) sol->ref(); sol->currentSender = this; } - if ( c->memberType() == QSIGNAL_CODE ) - object->qt_emit( c->member(), o ); - else - object->qt_invoke( c->member(), o ); + if ( c->memberType() == QSIGNAL_CODE ) { + if (object->d->ownThread == currentThread) { + object->qt_emit( c->member(), o ); + } + else { + if (object->d->ownThread && !object->d->ownThread->finished()) { + QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallEmit)); + } + } + } + else { + if (object->d->ownThread == currentThread) { + object->qt_invoke( c->member(), o ); + } + else { + if (object->d->ownThread && !object->d->ownThread->finished()) { + QApplication::postEvent(object, new QMetaCallEvent(c->member(), this, deepCopyQUObjectArray(o), QMetaCallEvent::MetaCallInvoke)); + } + } + } if (sol ) { sol->currentSender = oldSender; if ( sol->deref() ) @@ -2435,39 +2674,42 @@ void QObject::activate_signal( QConnectionList *clist, QUObject *o ) */ #ifndef QT_NO_PRELIMINARY_SIGNAL_SPY -#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ -void QObject::FNAME( int signal, TYPE param ) \ -{ \ - if ( qt_preliminary_signal_spy ) { \ - if ( !signalsBlocked() && signal >= 0 && \ - ( !connections || !connections->at( signal ) ) ) { \ - QUObject o[2]; \ - static_QUType_##TYPE.set( o+1, param ); \ - qt_spy_signal( this, signal, o ); \ - return; \ - } \ - } \ - if ( !connections || signalsBlocked() || signal < 0 ) \ - return; \ - QConnectionList *clist = connections->at( signal ); \ - if ( !clist ) \ - return; \ - QUObject o[2]; \ - static_QUType_##TYPE.set( o+1, param ); \ - activate_signal( clist, o ); \ +#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ +void QObject::FNAME( int signal, TYPE param ) \ +{ \ + if ( qt_preliminary_signal_spy ) { \ + if ( !signalsBlocked() && signal >= 0 && \ + ( !connections || !connections->at( signal ) ) ) { \ + QUObject o[2]; \ + o[1].isLastObject = true; \ + static_QUType_##TYPE.set( o+1, param ); \ + qt_spy_signal( this, signal, o ); \ + return; \ + } \ + } \ + if ( !connections || signalsBlocked() || signal < 0 ) \ + return; \ + QConnectionList *clist = connections->at( signal ); \ + if ( !clist ) \ + return; \ + QUObject o[2]; \ + o[1].isLastObject = true; \ + static_QUType_##TYPE.set( o+1, param ); \ + activate_signal( clist, o ); \ } #else -#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ -void QObject::FNAME( int signal, TYPE param ) \ -{ \ - if ( !connections || signalsBlocked() || signal < 0 ) \ - return; \ - QConnectionList *clist = connections->at( signal ); \ - if ( !clist ) \ - return; \ - QUObject o[2]; \ - static_QUType_##TYPE.set( o+1, param ); \ - activate_signal( clist, o ); \ +#define ACTIVATE_SIGNAL_WITH_PARAM(FNAME,TYPE) \ +void QObject::FNAME( int signal, TYPE param ) \ +{ \ + if ( !connections || signalsBlocked() || signal < 0 ) \ + return; \ + QConnectionList *clist = connections->at( signal ); \ + if ( !clist ) \ + return; \ + QUObject o[2]; \ + o[1].isLastObject = true; \ + static_QUType_##TYPE.set( o+1, param ); \ + activate_signal( clist, o ); \ } #endif -- cgit v1.2.3