summaryrefslogtreecommitdiffstats
path: root/src/kernel/qobject.cpp
diff options
context:
space:
mode:
authorTimothy Pearson <[email protected]>2012-12-06 16:47:27 -0600
committerTimothy Pearson <[email protected]>2012-12-06 16:47:27 -0600
commit78125ea2f051107b84fdc0354acdedb7885308ee (patch)
treec162d7f55467f81a01e2e669ce297acce06b3947 /src/kernel/qobject.cpp
parent7aa5ac7f0e76c5b87e4ca837b75b3edd522a3372 (diff)
downloadqt3-78125ea2f051107b84fdc0354acdedb7885308ee.tar.gz
qt3-78125ea2f051107b84fdc0354acdedb7885308ee.zip
Add real threading support, including per-thread event loops, to QThread
Diffstat (limited to 'src/kernel/qobject.cpp')
-rw-r--r--src/kernel/qobject.cpp328
1 files changed, 285 insertions, 43 deletions
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 <qmutex.h>
+#include "qmutex.h"
#include <private/qmutexpool_p.h>
+#include "qthread.h"
#endif
#include <ctype.h>
-
+#include <stdlib.h>
#ifndef QT_NO_USERDATA
class QObjectPrivate : public QPtrVector<QObjectUserData>
+#else
+class QObjectPrivate {
+#endif
{
public:
+#ifndef QT_NO_USERDATA
QObjectPrivate( uint s ) : QPtrVector<QObjectUserData>(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; i<count; i++) {
+ (origArray+i)->deepCopy(newArray+i);
+ }
+ return newArray;
+}
+
+/*! \internal */
+void destroyDeepCopiedQUObjectArray(QUObject* uArray)
+{
+ int count = 0;
+ while (!((uArray+count)->isLastObject)) {
+ count++;
+ }
+ count++;
+ for (int i=0; i<count; i++) {
+ (uArray+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<QMetaCallEvent*>(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