diff options
author | Timothy Pearson <[email protected]> | 2012-12-06 16:47:27 -0600 |
---|---|---|
committer | Timothy Pearson <[email protected]> | 2012-12-06 16:47:27 -0600 |
commit | 78125ea2f051107b84fdc0354acdedb7885308ee (patch) | |
tree | c162d7f55467f81a01e2e669ce297acce06b3947 /src/kernel | |
parent | 7aa5ac7f0e76c5b87e4ca837b75b3edd522a3372 (diff) | |
download | qt3-78125ea2f051107b84fdc0354acdedb7885308ee.tar.gz qt3-78125ea2f051107b84fdc0354acdedb7885308ee.zip |
Add real threading support, including per-thread event loops, to QThread
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/qapplication.cpp | 124 | ||||
-rw-r--r-- | src/kernel/qapplication.h | 13 | ||||
-rw-r--r-- | src/kernel/qevent.h | 2 | ||||
-rw-r--r-- | src/kernel/qeventloop.cpp | 32 | ||||
-rw-r--r-- | src/kernel/qeventloop_unix.cpp | 1 | ||||
-rw-r--r-- | src/kernel/qeventloop_unix_glib.cpp | 20 | ||||
-rw-r--r-- | src/kernel/qeventloop_x11.cpp | 98 | ||||
-rw-r--r-- | src/kernel/qeventloop_x11_glib.cpp | 600 | ||||
-rw-r--r-- | src/kernel/qobject.cpp | 328 | ||||
-rw-r--r-- | src/kernel/qobject.h | 16 | ||||
-rw-r--r-- | src/kernel/qthread.cpp | 17 | ||||
-rw-r--r-- | src/kernel/qthread.h | 14 | ||||
-rw-r--r-- | src/kernel/qthread_unix.cpp | 46 | ||||
-rw-r--r-- | src/kernel/qwidget.cpp | 9 |
14 files changed, 883 insertions, 437 deletions
diff --git a/src/kernel/qapplication.cpp b/src/kernel/qapplication.cpp index 51aa247..a9c9fb8 100644 --- a/src/kernel/qapplication.cpp +++ b/src/kernel/qapplication.cpp @@ -68,6 +68,7 @@ #if defined(QT_THREAD_SUPPORT) # include "qmutex.h" # include "qthread.h" +# include <private/qthreadinstance_p.h> #endif // QT_THREAD_SUPPORT #include <stdlib.h> @@ -383,7 +384,25 @@ Q_EXPORT Qt::HANDLE qt_get_application_thread_id() } #endif // QT_THREAD_SUPPORT +#ifndef QT_THREAD_SUPPORT QEventLoop *QApplication::eventloop = 0; // application event loop +#endif + +#ifdef QT_THREAD_SUPPORT +QEventLoop* QApplication::currentEventLoop() { + QThread* thread = QThread::currentThreadObject(); + if (thread) { + if (thread->d) { + return thread->d->eventLoop; + } + } + return NULL; +} +#else +QEventLoop* QApplication::currentEventLoop() { + return QApplication::eventloop; +} +#endif #ifndef QT_NO_ACCEL extern bool qt_dispatchAccelEvent( QWidget*, QKeyEvent* ); // def in qaccel.cpp @@ -516,6 +535,41 @@ QClipboard *qt_clipboard = 0; // global clipboard object #endif QWidgetList * qt_modal_stack=0; // stack of modal widgets +#ifdef QT_THREAD_SUPPORT +// thread wrapper for the main() thread +class QCoreApplicationThread : public QThread +{ +public: + inline QCoreApplicationThread() + { + QThreadInstance::setCurrentThread(this); + + // thread should be running and not finished for the lifetime + // of the application (even if QCoreApplication goes away) + d->running = true; + d->finished = false; + d->eventLoop = NULL; + } + inline ~QCoreApplicationThread() + { + // avoid warning from QThread + d->running = false; + } +private: + inline void run() + { + // this function should never be called, it is implemented + // only so that we can instantiate the object + qFatal("QCoreApplicationThread: internal error"); + } +}; + +static QCoreApplicationThread qt_main_thread; +static QThread *mainThread() { return &qt_main_thread; } +#else +static QThread* mainThread() { return QThread::currentThread(); } +#endif + // Definitions for posted events struct QPostEvent { QPostEvent( QObject *r, QEvent *e ): receiver( r ), event( e ) {} @@ -818,8 +872,8 @@ void QApplication::construct( int &argc, char **argv, Type type ) initialize( argc, argv ); if ( qt_is_gui_used ) qt_maxWindowRect = desktop()->rect(); - if ( eventloop ) - eventloop->appStartingUp(); + if ( currentEventLoop() ) + currentEventLoop()->appStartingUp(); } /*! @@ -874,8 +928,8 @@ QApplication::QApplication( Display* dpy, HANDLE visual, HANDLE colormap ) if ( qt_is_gui_used ) qt_maxWindowRect = desktop()->rect(); - if ( eventloop ) - eventloop->appStartingUp(); + if ( currentEventLoop() ) + currentEventLoop()->appStartingUp(); } /*! @@ -916,13 +970,26 @@ QApplication::QApplication(Display *dpy, int argc, char **argv, if ( qt_is_gui_used ) qt_maxWindowRect = desktop()->rect(); - if ( eventloop ) - eventloop->appStartingUp(); + if ( currentEventLoop() ) + currentEventLoop()->appStartingUp(); } #endif // Q_WS_X11 +#ifdef QT_THREAD_SUPPORT +QThread* QApplication::guiThread() { + return mainThread(); +} + +bool QApplication::isGuiThread() { + return (QThread::currentThreadObject() == guiThread()); +} +#else +bool QApplication::isGuiThread() { + return true; +} +#endif void QApplication::init_precmdline() { @@ -1030,8 +1097,8 @@ QApplication::~QApplication() } #endif - if ( eventloop ) - eventloop->appClosingDown(); + if ( currentEventLoop() ) + currentEventLoop()->appClosingDown(); if ( postRList ) { QVFuncList::Iterator it = postRList->begin(); while ( it != postRList->end() ) { // call post routines @@ -2698,8 +2765,20 @@ bool QApplication::internalNotify( QObject *receiver, QEvent * e) } - if (!handled) + if (!handled) { +#if defined(QT_THREAD_SUPPORT) + bool locked = QApplication::qt_mutex->locked(); + if (locked) { + QApplication::qt_mutex->unlock(); + } +#endif consumed = receiver->event( e ); +#if defined(QT_THREAD_SUPPORT) + if (locked) { + QApplication::qt_mutex->lock(); + } +#endif + } e->spont = FALSE; return consumed; } @@ -2793,9 +2872,10 @@ void QApplication::processOneEvent() */ QEventLoop *QApplication::eventLoop() { - if ( !eventloop && !is_app_closing ) + if ( !currentEventLoop() && !is_app_closing ) { (void) new QEventLoop( qApp, "default event loop" ); - return eventloop; + } + return currentEventLoop(); } @@ -3263,8 +3343,23 @@ void QApplication::postEvent( QObject *receiver, QEvent *event ) l->append( pe ); globalPostedEvents->append( pe ); - if (eventloop) - eventloop->wakeUp(); +#ifdef QT_THREAD_SUPPORT + if ( event->type() == QEvent::MetaCall ) { + // Wake up the receiver thread event loop + QThread* thread = receiver->contextThreadObject(); + if (thread) { + if (thread->d) { + if (thread->d->eventLoop) { + thread->d->eventLoop->wakeUp(); + } + } + } + return; + } +#endif + + if (currentEventLoop()) + currentEventLoop()->wakeUp(); } @@ -3326,7 +3421,8 @@ void QApplication::sendPostedEvents( QObject *receiver, int event_type ) && ( receiver == 0 // we send to all receivers || receiver == pe->receiver ) // we send to THAT receiver && ( event_type == 0 // we send all types - || event_type == pe->event->type() ) ) { // we send THAT type + || event_type == pe->event->type() ) // we send THAT type + && ( (!pe->receiver) || (pe->receiver->contextThreadObject() == QThread::currentThreadObject()) ) ) { // only send if active thread is receiver object owning thread // first, we diddle the event so that we can deliver // it, and that noone will try to touch it later. pe->event->posted = FALSE; diff --git a/src/kernel/qapplication.h b/src/kernel/qapplication.h index c34ff45..5611164 100644 --- a/src/kernel/qapplication.h +++ b/src/kernel/qapplication.h @@ -63,6 +63,7 @@ class QWSDecoration; #ifdef QT_THREAD_SUPPORT class QMutex; +class QThread; #endif // QT_THREAD_SUPPORT @@ -369,7 +370,9 @@ private: #ifndef QT_NO_CURSOR static QCursor *app_cursor; #endif +#ifndef QT_THREAD_SUPPORT static QEventLoop* eventloop; +#endif static int app_tracking; static bool is_app_running; static bool is_app_closing; @@ -425,6 +428,7 @@ private: static void removePostedEvent( QEvent * ); static void removePostedEvents( QObject *receiver, int event_type ); + friend class QObject; friend class QWidget; friend class QETWidget; friend class QDialog; @@ -444,6 +448,15 @@ private: // Disabled copy constructor and operator= QApplication( const QApplication & ); QApplication &operator=( const QApplication & ); #endif + +private: + static QEventLoop* currentEventLoop(); + +public: +#ifdef QT_THREAD_SUPPORT + static QThread* guiThread(); +#endif + static bool isGuiThread(); }; inline int QApplication::argc() const diff --git a/src/kernel/qevent.h b/src/kernel/qevent.h index 6512b9a..9587b8f 100644 --- a/src/kernel/qevent.h +++ b/src/kernel/qevent.h @@ -137,6 +137,8 @@ public: HelpRequest = 95, // CE (?) button pressed WindowStateChange = 96, // window state has changed IconDrag = 97, // proxy icon dragged + MetaCall = 98, // meta method call (internal) + ThreadChange = 99, // thread changed User = 1000, // first user event id MaxUser = 65535 // last user event id }; diff --git a/src/kernel/qeventloop.cpp b/src/kernel/qeventloop.cpp index 1f6a130..5eadb9e 100644 --- a/src/kernel/qeventloop.cpp +++ b/src/kernel/qeventloop.cpp @@ -41,6 +41,11 @@ #include "qapplication.h" #include "qdatetime.h" +#ifdef QT_THREAD_SUPPORT +# include "qthread.h" +# include "qthreadinstance_p.h" +#endif + /*! \class QEventLoop \brief The QEventLoop class manages the event queue. @@ -100,15 +105,27 @@ QEventLoop::QEventLoop( QObject *parent, const char *name ) : QObject( parent, name ) { #if defined(QT_CHECK_STATE) - if ( QApplication::eventloop ) - qFatal( "QEventLoop: there must be only one event loop object. \nConstruct it before QApplication." ); - // for now ;) + if ( QApplication::currentEventLoop() ) + qFatal( "QEventLoop: there must be only one event loop object per thread. \nIf this is supposed to be the main GUI event loop, construct it before QApplication." ); + if (!QThread::currentThreadObject()) { + qFatal( "QEventLoop: this object can only be used in threads constructed via QThread." ); + } #endif // QT_CHECK_STATE d = new QEventLoopPrivate; init(); + +#ifdef QT_THREAD_SUPPORT + QThread* thread = QThread::currentThreadObject(); + if (thread) { + if (thread->d) { + thread->d->eventLoop = this; + } + } +#else QApplication::eventloop = this; +#endif } /*! @@ -118,7 +135,16 @@ QEventLoop::~QEventLoop() { cleanup(); delete d; +#ifdef QT_THREAD_SUPPORT + QThread* thread = QThread::currentThreadObject(); + if (thread) { + if (thread->d) { + thread->d->eventLoop = 0; + } + } +#else QApplication::eventloop = 0; +#endif } /*! diff --git a/src/kernel/qeventloop_unix.cpp b/src/kernel/qeventloop_unix.cpp index b0ad8b9..202ef12 100644 --- a/src/kernel/qeventloop_unix.cpp +++ b/src/kernel/qeventloop_unix.cpp @@ -40,6 +40,7 @@ #include "qeventloop.h" #include "qapplication.h" #include "qbitarray.h" +#include "qmutex.h" #include <stdlib.h> #include <sys/types.h> diff --git a/src/kernel/qeventloop_unix_glib.cpp b/src/kernel/qeventloop_unix_glib.cpp index e607447..9d3ce27 100644 --- a/src/kernel/qeventloop_unix_glib.cpp +++ b/src/kernel/qeventloop_unix_glib.cpp @@ -44,6 +44,7 @@ #include "qeventloop.h" #include "qapplication.h" #include "qbitarray.h" +#include "qmutex.h" #include <stdlib.h> #include <sys/types.h> @@ -578,17 +579,14 @@ int QEventLoop::activateSocketNotifiers() while ( (sn=it.current()) ) { ++it; d->sn_pending_list.removeRef( sn ); - if ( sn->pending ) { - - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("activate sn : send event fd=%d\n", sn->gPollFD.fd ); - #endif - - - sn->pending = FALSE; - QApplication::sendEvent( sn->obj, &event ); - n_act++; - } + if ( sn->pending ) { +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("activate sn : send event fd=%d\n", sn->gPollFD.fd ); +#endif + sn->pending = FALSE; + QApplication::sendEvent( sn->obj, &event ); + n_act++; + } } return n_act; diff --git a/src/kernel/qeventloop_x11.cpp b/src/kernel/qeventloop_x11.cpp index 833be69..cd3e865 100644 --- a/src/kernel/qeventloop_x11.cpp +++ b/src/kernel/qeventloop_x11.cpp @@ -146,55 +146,57 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) if ( qt_is_gui_used ) { QApplication::sendPostedEvents(); - // Two loops so that posted events accumulate - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - // also flushes output buffer - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - if ( d->shortcut ) { - return FALSE; - } - - XNextEvent( QPaintDevice::x11AppDisplay(), &event ); - - if ( flags & ExcludeUserInput ) { - switch ( event.type ) { - case ButtonPress: - case ButtonRelease: - case MotionNotify: - case XKeyPress: - case XKeyRelease: - case EnterNotify: - case LeaveNotify: - continue; - - case ClientMessage: - { - // from qapplication_x11.cpp - extern Atom qt_wm_protocols; - extern Atom qt_wm_take_focus; - extern Atom qt_qt_scrolldone; - - // only keep the wm_take_focus and - // qt_qt_scrolldone protocols, discard all - // other client messages - if ( event.xclient.format != 32 ) - continue; - - if ( event.xclient.message_type == qt_wm_protocols || - (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) - break; - if ( event.xclient.message_type == qt_qt_scrolldone ) - break; + if (QApplication::isGuiThread()) { + // Two loops so that posted events accumulate + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + // also flushes output buffer + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + if ( d->shortcut ) { + return FALSE; + } + + XNextEvent( QPaintDevice::x11AppDisplay(), &event ); + + if ( flags & ExcludeUserInput ) { + switch ( event.type ) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case XKeyPress: + case XKeyRelease: + case EnterNotify: + case LeaveNotify: + continue; + + case ClientMessage: + { + // from qapplication_x11.cpp + extern Atom qt_wm_protocols; + extern Atom qt_wm_take_focus; + extern Atom qt_qt_scrolldone; + + // only keep the wm_take_focus and + // qt_qt_scrolldone protocols, discard all + // other client messages + if ( event.xclient.format != 32 ) + continue; + + if ( event.xclient.message_type == qt_wm_protocols || + (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) + break; + if ( event.xclient.message_type == qt_qt_scrolldone ) + break; + } + + default: break; + } + } + + nevents++; + if ( qApp->x11ProcessEvent( &event ) == 1 ) + return TRUE; } - - default: break; - } } - - nevents++; - if ( qApp->x11ProcessEvent( &event ) == 1 ) - return TRUE; - } } } @@ -261,7 +263,7 @@ bool QEventLoop::processEvents( ProcessEventsFlags flags ) FD_ZERO( &d->sn_vec[2].select_fds ); } - if ( qt_is_gui_used ) { + if ( qt_is_gui_used && QApplication::isGuiThread() ) { // select for events on the event socket - only on X11 FD_SET( d->xfd, &d->sn_vec[0].select_fds ); highest = QMAX( highest, d->xfd ); diff --git a/src/kernel/qeventloop_x11_glib.cpp b/src/kernel/qeventloop_x11_glib.cpp index 59ab8a1..b3d881a 100644 --- a/src/kernel/qeventloop_x11_glib.cpp +++ b/src/kernel/qeventloop_x11_glib.cpp @@ -39,7 +39,6 @@ ** **********************************************************************/ - #include "qeventloop_glib_p.h" // includes qplatformdefs.h #include "qeventloop.h" #include "qapplication.h" @@ -58,23 +57,21 @@ // Qt-GSource Structure and Callbacks typedef struct { - GSource source; - QEventLoop * qeventLoop; + GSource source; + QEventLoop * qeventLoop; } QtGSource; -static gboolean qt_gsource_prepare ( GSource *source, - gint *timeout ); +static gboolean qt_gsource_prepare ( GSource *source, gint *timeout ); static gboolean qt_gsource_check ( GSource *source ); -static gboolean qt_gsource_dispatch ( GSource *source, - GSourceFunc callback, gpointer user_data ); +static gboolean qt_gsource_dispatch ( GSource *source, GSourceFunc callback, gpointer user_data ); static GSourceFuncs qt_gsource_funcs = { - qt_gsource_prepare, - qt_gsource_check, - qt_gsource_dispatch, - NULL, - NULL, - NULL + qt_gsource_prepare, + qt_gsource_check, + qt_gsource_dispatch, + NULL, + NULL, + NULL }; // forward main loop callbacks to QEventLoop methods! @@ -82,25 +79,25 @@ static GSourceFuncs qt_gsource_funcs = { static gboolean qt_gsource_prepare ( GSource *source, gint *timeout ) { - QtGSource * qtGSource; + QtGSource * qtGSource; qtGSource = (QtGSource*) source; - return qtGSource->qeventLoop->gsourcePrepare(source, timeout); + return qtGSource->qeventLoop->gsourcePrepare(source, timeout); } static gboolean qt_gsource_check ( GSource *source ) { - QtGSource * qtGSource = (QtGSource*) source; - return qtGSource->qeventLoop->gsourceCheck(source); + QtGSource * qtGSource = (QtGSource*) source; + return qtGSource->qeventLoop->gsourceCheck(source); } static gboolean qt_gsource_dispatch ( GSource *source, GSourceFunc callback, gpointer user_data ) { - Q_UNUSED(callback); - Q_UNUSED(user_data); - - QtGSource * qtGSource = (QtGSource*) source; - return qtGSource->qeventLoop->gsourceDispatch(source); + Q_UNUSED(callback); + Q_UNUSED(user_data); + + QtGSource * qtGSource = (QtGSource*) source; + return qtGSource->qeventLoop->gsourceDispatch(source); } @@ -134,82 +131,84 @@ static QVFuncList *qt_postselect_handler = 0; void qt_install_preselect_handler( VFPTR handler ) { - if ( !qt_preselect_handler ) - qt_preselect_handler = new QVFuncList; - qt_preselect_handler->append( handler ); + if ( !qt_preselect_handler ) { + qt_preselect_handler = new QVFuncList; + } + qt_preselect_handler->append( handler ); } + void qt_remove_preselect_handler( VFPTR handler ) { - if ( qt_preselect_handler ) { - QVFuncList::Iterator it = qt_preselect_handler->find( handler ); - if ( it != qt_preselect_handler->end() ) - qt_preselect_handler->remove( it ); - } + if ( qt_preselect_handler ) { + QVFuncList::Iterator it = qt_preselect_handler->find( handler ); + if ( it != qt_preselect_handler->end() ) { + qt_preselect_handler->remove( it ); + } + } } + void qt_install_postselect_handler( VFPTR handler ) { - if ( !qt_postselect_handler ) - qt_postselect_handler = new QVFuncList; - qt_postselect_handler->prepend( handler ); + if ( !qt_postselect_handler ) { + qt_postselect_handler = new QVFuncList; + } + qt_postselect_handler->prepend( handler ); } + void qt_remove_postselect_handler( VFPTR handler ) { - if ( qt_postselect_handler ) { - QVFuncList::Iterator it = qt_postselect_handler->find( handler ); - if ( it != qt_postselect_handler->end() ) - qt_postselect_handler->remove( it ); - } + if ( qt_postselect_handler ) { + QVFuncList::Iterator it = qt_postselect_handler->find( handler ); + if ( it != qt_postselect_handler->end() ) { + qt_postselect_handler->remove( it ); + } + } } - void QEventLoop::init() { // initialize ProcessEventFlags (all events & wait for more) - d->pev_flags = AllEvents | WaitForMore; - // initialize the common parts of the event loop - if (pipe( d->thread_pipe ) < 0) { - // Error! - } - fcntl(d->thread_pipe[0], F_SETFD, FD_CLOEXEC); - fcntl(d->thread_pipe[1], F_SETFD, FD_CLOEXEC); + // initialize the common parts of the event loop + if (pipe( d->thread_pipe ) < 0) { + // Error! + } + fcntl(d->thread_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(d->thread_pipe[1], F_SETFD, FD_CLOEXEC); - // intitialize the X11 parts of the event loop - d->xfd = -1; - if ( qt_is_gui_used ) - d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); + // intitialize the X11 parts of the event loop + d->xfd = -1; + if ( qt_is_gui_used && QApplication::isGuiThread() ) { + d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); + } - // new GSource + // new GSource + QtGSource * qtGSource = (QtGSource*) g_source_new(&qt_gsource_funcs, sizeof(QtGSource)); - QtGSource * qtGSource = (QtGSource*) g_source_new(&qt_gsource_funcs, - sizeof(QtGSource)); - - g_source_set_can_recurse ((GSource*)qtGSource, TRUE); + g_source_set_can_recurse ((GSource*)qtGSource, TRUE); + + qtGSource->qeventLoop = this; - qtGSource->qeventLoop = this; - // init main loop and attach gsource - - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside init(1)\n"); - #endif - - g_main_loop_new (NULL, 1); - - g_source_attach( (GSource*)qtGSource, NULL ); + +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside init(1)\n"); +#endif + + g_main_loop_new (NULL, 1); + + g_source_attach( (GSource*)qtGSource, NULL ); d->gSource = (GSource*) qtGSource; // poll for X11 events - if ( qt_is_gui_used ) { - - + if ( qt_is_gui_used && QApplication::isGuiThread() ) { d->x_gPollFD.fd = d->xfd; d->x_gPollFD.events = G_IO_IN | G_IO_HUP; g_source_add_poll(d->gSource, &d->x_gPollFD); - } + } // poll thread-pipe @@ -218,21 +217,21 @@ void QEventLoop::init() g_source_add_poll(d->gSource, &d->threadPipe_gPollFD); - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside init(2)\n"); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside init(2)\n"); +#endif } void QEventLoop::cleanup() { - // cleanup the common parts of the event loop - close( d->thread_pipe[0] ); - close( d->thread_pipe[1] ); - cleanupTimers(); - - // cleanup the X11 parts of the event loop - d->xfd = -1; + // cleanup the common parts of the event loop + close( d->thread_pipe[0] ); + close( d->thread_pipe[1] ); + cleanupTimers(); + + // cleanup the X11 parts of the event loop + d->xfd = -1; // todo: destroy gsource } @@ -240,186 +239,189 @@ void QEventLoop::cleanup() bool QEventLoop::processEvents( ProcessEventsFlags flags ) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside processEvents(1) looplevel=%d\n", d->looplevel ); - #endif - ProcessEventsFlags save_flags; - int rval; - save_flags = d->pev_flags; +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside processEvents(1) looplevel=%d\n", d->looplevel ); +#endif - d->pev_flags = flags; - - rval = g_main_context_iteration(NULL, flags & WaitForMore ? TRUE : FALSE); - - d->pev_flags = save_flags; + ProcessEventsFlags save_flags; + int rval; + save_flags = d->pev_flags; - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside processEvents(2) looplevel=%d rval=%d\n", d->looplevel, rval ); - #endif + d->pev_flags = flags; - return rval; // were events processed? + rval = g_main_context_iteration(NULL, flags & WaitForMore ? TRUE : FALSE); + + d->pev_flags = save_flags; + +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside processEvents(2) looplevel=%d rval=%d\n", d->looplevel, rval ); +#endif + + return rval; // were events processed? } bool QEventLoop::processX11Events() { ProcessEventsFlags flags = d->pev_flags; - // process events from the X server - XEvent event; - int nevents = 0; + // process events from the X server + XEvent event; + int nevents = 0; #if defined(QT_THREAD_SUPPORT) - QMutexLocker locker( QApplication::qt_mutex ); + QMutexLocker locker( QApplication::qt_mutex ); #endif - // handle gui and posted events - if ( qt_is_gui_used ) { - QApplication::sendPostedEvents(); - - // Two loops so that posted events accumulate - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - // also flushes output buffer - while ( XPending( QPaintDevice::x11AppDisplay() ) ) { - if ( d->shortcut ) { - return FALSE; - } - - XNextEvent( QPaintDevice::x11AppDisplay(), &event ); - - if ( flags & ExcludeUserInput ) { - switch ( event.type ) { - case ButtonPress: - case ButtonRelease: - case MotionNotify: - case XKeyPress: - case XKeyRelease: - case EnterNotify: - case LeaveNotify: - continue; - - case ClientMessage: - { - // from qapplication_x11.cpp - extern Atom qt_wm_protocols; - extern Atom qt_wm_take_focus; - extern Atom qt_qt_scrolldone; - - // only keep the wm_take_focus and - // qt_qt_scrolldone protocols, discard all - // other client messages - if ( event.xclient.format != 32 ) - continue; - - if ( event.xclient.message_type == qt_wm_protocols || - (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) - break; - if ( event.xclient.message_type == qt_qt_scrolldone ) - break; + // handle gui and posted events + if ( qt_is_gui_used ) { + QApplication::sendPostedEvents(); + + if (QApplication::isGuiThread()) { + // Two loops so that posted events accumulate + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + // also flushes output buffer + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + if ( d->shortcut ) { + return FALSE; + } + + XNextEvent( QPaintDevice::x11AppDisplay(), &event ); + + if ( flags & ExcludeUserInput ) { + switch ( event.type ) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case XKeyPress: + case XKeyRelease: + case EnterNotify: + case LeaveNotify: + continue; + + case ClientMessage: + { + // from qapplication_x11.cpp + extern Atom qt_wm_protocols; + extern Atom qt_wm_take_focus; + extern Atom qt_qt_scrolldone; + + // only keep the wm_take_focus and + // qt_qt_scrolldone protocols, discard all + // other client messages + if ( event.xclient.format != 32 ) + continue; + + if ( event.xclient.message_type == qt_wm_protocols || + (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) + break; + if ( event.xclient.message_type == qt_qt_scrolldone ) + break; + } + + default: break; + } + } + + nevents++; + if ( qApp->x11ProcessEvent( &event ) == 1 ) + return TRUE; + } } - - default: break; - } } - - nevents++; - if ( qApp->x11ProcessEvent( &event ) == 1 ) - return TRUE; - } } - } - - if ( d->shortcut ) { + + if ( d->shortcut ) { + return FALSE; + } + + QApplication::sendPostedEvents(); + + const uint exclude_all = ExcludeSocketNotifiers | 0x08; + // 0x08 == ExcludeTimers for X11 only + if ( nevents > 0 && ( flags & exclude_all ) == exclude_all && ( flags & WaitForMore ) ) { + return TRUE; + } return FALSE; - } - - QApplication::sendPostedEvents(); - - const uint exclude_all = ExcludeSocketNotifiers | 0x08; - // 0x08 == ExcludeTimers for X11 only - if ( nevents > 0 && ( flags & exclude_all ) == exclude_all && - ( flags & WaitForMore ) ) { - return TRUE; - } - return FALSE; } - - + + bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) { Q_UNUSED(gs); - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourcePrepare(1)\n"); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(1)\n"); +#endif ProcessEventsFlags flags = d->pev_flags; - + #if defined(QT_THREAD_SUPPORT) - QMutexLocker locker( QApplication::qt_mutex ); + QMutexLocker locker( QApplication::qt_mutex ); #endif - - // don't block if exitLoop() or exit()/quit() has been called. - bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore); - - // Process timers and socket notifiers - the common UNIX stuff - - // return the maximum time we can wait for an event. - static timeval zerotm; - timeval *tm = 0; - if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only - tm = qt_wait_timer(); // wait for timer or X event - if ( !canWait ) { - if ( !tm ) - tm = &zerotm; - tm->tv_sec = 0; // no time to wait - tm->tv_usec = 0; - } - } - - // include or exclude SocketNotifiers (by setting or cleaning poll events) - - if ( ! ( flags & ExcludeSocketNotifiers ) ) { - QPtrListIterator<QSockNotGPollFD> it( d->sn_list ); - QSockNotGPollFD *sn; - while ( (sn=it.current()) ) { - ++it; - sn->gPollFD.events = sn->events; // restore poll events + + // don't block if exitLoop() or exit()/quit() has been called. + bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore); + + // Process timers and socket notifiers - the common UNIX stuff + + // return the maximum time we can wait for an event. + static timeval zerotm; + timeval *tm = 0; + if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only + tm = qt_wait_timer(); // wait for timer or X event + if ( !canWait ) { + if ( !tm ) { + tm = &zerotm; + } + tm->tv_sec = 0; // no time to wait + tm->tv_usec = 0; } - } else { - QPtrListIterator<QSockNotGPollFD> it( d->sn_list ); - QSockNotGPollFD *sn; - while ( (sn=it.current()) ) { - ++it; - sn->gPollFD.events = 0; // delete poll events + } + + // include or exclude SocketNotifiers (by setting or cleaning poll events) + if ( ! ( flags & ExcludeSocketNotifiers ) ) { + QPtrListIterator<QSockNotGPollFD> it( d->sn_list ); + QSockNotGPollFD *sn; + while ( (sn=it.current()) ) { + ++it; + sn->gPollFD.events = sn->events; // restore poll events + } + } + else { + QPtrListIterator<QSockNotGPollFD> it( d->sn_list ); + QSockNotGPollFD *sn; + while ( (sn=it.current()) ) { + ++it; + sn->gPollFD.events = 0; // delete poll events } } - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourcePrepare(2) canwait=%d\n", canWait); - #endif - - if ( canWait ) - emit aboutToBlock(); - +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(2) canwait=%d\n", canWait); +#endif - if ( qt_preselect_handler ) { - QVFuncList::Iterator it, end = qt_preselect_handler->end(); - for ( it = qt_preselect_handler->begin(); it != end; ++it ) - (**it)(); - } + if ( canWait ) { + emit aboutToBlock(); + } - // unlock the GUI mutex and select. when we return from this function, there is - // something for us to do + if ( qt_preselect_handler ) { + QVFuncList::Iterator it, end = qt_preselect_handler->end(); + for ( it = qt_preselect_handler->begin(); it != end; ++it ) + (**it)(); + } + + // unlock the GUI mutex and select. when we return from this function, there is + // something for us to do #if defined(QT_THREAD_SUPPORT) - locker.mutex()->unlock(); + locker.mutex()->unlock(); #endif - - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait); - #endif - + +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait); +#endif + // do we have to dispatch events? - if (hasPendingEvents()) { + if (hasPendingEvents()) { *timeout = 0; // no time to stay in poll #ifdef DEBUG_QT_GLIBMAINLOOP @@ -427,8 +429,8 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) #endif return FALSE; - } - + } + // stay in poll until something happens? if (!tm) { // fixme *timeout = -1; // wait forever @@ -439,25 +441,24 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) return FALSE; } - + // else timeout >=0 *timeout = tm->tv_sec * 1000 + tm->tv_usec/1000; - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourcePrepare(3c) timeout=%d \n", *timeout); - #endif - +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(3c) timeout=%d \n", *timeout); +#endif - return FALSE; + return FALSE; } bool QEventLoop::gsourceCheck(GSource *gs) { Q_UNUSED(gs); - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(1)\n"); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(1)\n"); +#endif // Socketnotifier events? @@ -476,45 +477,45 @@ bool QEventLoop::gsourceCheck(GSource *gs) { //} if (d->x_gPollFD.revents) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(2) xfd!\n"); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) xfd!\n"); +#endif return TRUE; // we got events! } - if (d->threadPipe_gPollFD.revents) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(2) threadpipe!!\n"); - #endif + if (d->threadPipe_gPollFD.revents) { +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) threadpipe!!\n"); +#endif return TRUE; // we got events! } - if (hasPendingEvents()) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(2) pendingEvents!\n"); - #endif + if (hasPendingEvents()) { +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) pendingEvents!\n"); +#endif return TRUE; // we got more X11 events! } - // check if we have timers to activate? + // check if we have timers to activate? timeval * tm =qt_wait_timer(); - if (tm && (tm->tv_sec == 0 && tm->tv_usec == 0 )) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(2) qtwaittimer!\n"); - #endif - - return TRUE; - } + if (tm && (tm->tv_sec == 0 && tm->tv_usec == 0 )) { +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) qtwaittimer!\n"); +#endif + + return TRUE; + } - // nothing to dispatch + // nothing to dispatch - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceCheck(2) nothing to dispatch!\n"); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) nothing to dispatch!\n"); +#endif - return FALSE; + return FALSE; } @@ -533,37 +534,35 @@ bool QEventLoop::gsourceDispatch(GSource *gs) { ProcessEventsFlags flags = d->pev_flags; - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceDispatch(1)\n"); - #endif - - // we are awake, broadcast it - emit awake(); - emit qApp->guiThreadAwake(); - - // some other thread woke us up... consume the data on the thread pipe so that - // select doesn't immediately return next time +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceDispatch(1)\n"); +#endif + // we are awake, broadcast it + emit awake(); + emit qApp->guiThreadAwake(); + + // some other thread woke us up... consume the data on the thread pipe so that + // select doesn't immediately return next time + if ( d->threadPipe_gPollFD.revents) { - char c; - if (::read( d->thread_pipe[0], &c, 1 ) < 0) { - // Error! - } - } + char c; + if (::read( d->thread_pipe[0], &c, 1 ) < 0) { + // Error! + } + } - if ( qt_postselect_handler ) { - QVFuncList::Iterator it, end = qt_postselect_handler->end(); - for ( it = qt_postselect_handler->begin(); it != end; ++it ) - (**it)(); - } + if ( qt_postselect_handler ) { + QVFuncList::Iterator it, end = qt_postselect_handler->end(); + for ( it = qt_postselect_handler->begin(); it != end; ++it ) + (**it)(); + } - // activate socket notifiers - if ( ! ( flags & ExcludeSocketNotifiers )) { + // activate socket notifiers + if ( ! ( flags & ExcludeSocketNotifiers )) { // if select says data is ready on any socket, then set the socket notifier // to pending // if ( &d->sn_list ) { - - QPtrList<QSockNotGPollFD> *list = &d->sn_list; QSockNotGPollFD *sn = list->first(); while ( sn ) { @@ -572,39 +571,35 @@ bool QEventLoop::gsourceDispatch(GSource *gs) { sn = list->next(); } // } - + nevents += activateSocketNotifiers(); - } + } - // activate timers - if ( ! ( flags & 0x08 ) ) { + // activate timers + if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only nevents += activateTimers(); - } - - + } // return true if we handled events, false otherwise //return (nevents > 0); // now process x11 events! - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside gsourceDispatch(2) hasPendingEvents=%d\n", hasPendingEvents()); - #endif +#ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceDispatch(2) hasPendingEvents=%d\n", hasPendingEvents()); +#endif if (hasPendingEvents()) { - // color approx. optimization - only on X11 qt_reset_color_avail(); processX11Events(); - } #if defined(QT_THREAD_SUPPORT) - locker.mutex()->unlock(); + locker.mutex()->unlock(); #endif if (d->singletoolkit) { @@ -617,21 +612,22 @@ bool QEventLoop::gsourceDispatch(GSource *gs) { bool QEventLoop::hasPendingEvents() const { - extern uint qGlobalPostedEventsCount(); // from qapplication.cpp - return ( qGlobalPostedEventsCount() || ( qt_is_gui_used ? XPending( QPaintDevice::x11AppDisplay() ) : 0)); + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return ( qGlobalPostedEventsCount() || ( (qt_is_gui_used && QApplication::isGuiThread()) ? XPending( QPaintDevice::x11AppDisplay() ) : 0)); } void QEventLoop::appStartingUp() { - if ( qt_is_gui_used ) - d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); + if ( qt_is_gui_used ) { + d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); + } } void QEventLoop::appClosingDown() { - d->xfd = -1; + d->xfd = -1; } void QEventLoop::setSingleToolkitEventHandling(bool enabled) { - d->singletoolkit = enabled; + d->singletoolkit = enabled; }
\ No newline at end of file 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 diff --git a/src/kernel/qobject.h b/src/kernel/qobject.h index 6de28db..2f469ba 100644 --- a/src/kernel/qobject.h +++ b/src/kernel/qobject.h @@ -63,6 +63,10 @@ class QObjectUserData; #endif struct QUObject; +#ifdef QT_THREAD_SUPPORT +class QThread; +#endif + class Q_EXPORT QObject: public Qt { Q_OBJECT @@ -217,6 +221,18 @@ private: // Disabled copy constructor and operator= QObject( const QObject & ); QObject &operator=( const QObject & ); #endif + +public: +#ifdef QT_THREAD_SUPPORT + QThread* contextThreadObject() const; + void moveToThread(QThread *targetThread); +#endif + +private: +#ifdef QT_THREAD_SUPPORT + void moveToThread_helper(QThread *targetThread); + void setThreadObject_helper(QThread *targetThread); +#endif }; diff --git a/src/kernel/qthread.cpp b/src/kernel/qthread.cpp index 1653a51..cfe8c56 100644 --- a/src/kernel/qthread.cpp +++ b/src/kernel/qthread.cpp @@ -41,6 +41,7 @@ #include "qplatformdefs.h" #include "qthread.h" +#include "qeventloop.h" #include <private/qthreadinstance_p.h> #ifndef QT_H @@ -238,4 +239,20 @@ void QThread::postEvent( QObject * receiver, QEvent * event ) } #endif +QEventLoopThread::QEventLoopThread() : QThread() +{ + // +} + +QEventLoopThread::~QEventLoopThread() +{ + // +} + +void QEventLoopThread::run() +{ + QEventLoop* eventLoop = QApplication::eventLoop(); + if (eventLoop) eventLoop->exec(); +} + #endif // QT_THREAD_SUPPORT diff --git a/src/kernel/qthread.h b/src/kernel/qthread.h index 160919f..0188ea6 100644 --- a/src/kernel/qthread.h +++ b/src/kernel/qthread.h @@ -118,11 +118,25 @@ protected: private: QThreadInstance * d; friend class QThreadInstance; + friend class QCoreApplicationThread; + friend class QApplication; + friend class QEventLoop; #if defined(Q_DISABLE_COPY) QThread( const QThread & ); QThread &operator=( const QThread & ); #endif // Q_DISABLE_COPY + +public: + static QThread* currentThreadObject(); +}; + +class Q_EXPORT QEventLoopThread : public QThread +{ + public: + QEventLoopThread(); + ~QEventLoopThread(); + virtual void run(); }; #endif // QT_THREAD_SUPPORT diff --git a/src/kernel/qthread_unix.cpp b/src/kernel/qthread_unix.cpp index e4d6625..52b070e 100644 --- a/src/kernel/qthread_unix.cpp +++ b/src/kernel/qthread_unix.cpp @@ -52,11 +52,6 @@ typedef pthread_mutex_t Q_MUTEX_T; #include <sched.h> -static QThreadInstance main_instance = { - 0, { 0, &main_instance }, 0, 0, 1, 0, PTHREAD_COND_INITIALIZER, 0 -}; - - static QMutexPool *qt_thread_mutexpool = 0; @@ -82,10 +77,20 @@ static void create_storage_key() ** QThreadInstance *************************************************************************/ +void QThreadInstance::setCurrentThread(QThread *thread) +{ + pthread_once(&storage_key_once, create_storage_key); + pthread_setspecific(storage_key, thread); +} + QThreadInstance *QThreadInstance::current() { + QThreadInstance *ret = NULL; pthread_once( &storage_key_once, create_storage_key ); - QThreadInstance *ret = (QThreadInstance *) pthread_getspecific( storage_key ); + QThread *thread = (QThread *) pthread_getspecific( storage_key ); + if (thread) { + ret = thread->d; + } return ret; } @@ -101,6 +106,8 @@ void QThreadInstance::init(unsigned int stackSize) pthread_cond_init(&thread_done, NULL); thread_id = 0; + eventLoop = 0; + // threads have not been initialized yet, do it now if (! qt_thread_mutexpool) QThread::initialize(); } @@ -114,8 +121,8 @@ void *QThreadInstance::start( void *_arg ) { void **arg = (void **) _arg; - pthread_once( &storage_key_once, create_storage_key ); - pthread_setspecific( storage_key, arg[1] ); + setCurrentThread( (QThread *) arg[0] ); + pthread_cleanup_push( QThreadInstance::finish, arg[1] ); pthread_testcancel(); @@ -192,9 +199,6 @@ void QThread::initialize() qt_global_mutexpool = new QMutexPool( TRUE, 73 ); if ( ! qt_thread_mutexpool ) qt_thread_mutexpool = new QMutexPool( FALSE, 127 ); - - pthread_once( &storage_key_once, create_storage_key ); - pthread_setspecific( storage_key, &main_instance ); } /*! \internal @@ -206,11 +210,6 @@ void QThread::cleanup() delete qt_thread_mutexpool; qt_global_mutexpool = 0; qt_thread_mutexpool = 0; - - QThreadInstance::finish(&main_instance); - - pthread_once( &storage_key_once, create_storage_key ); - pthread_setspecific( storage_key, 0 ); } /*! @@ -470,5 +469,20 @@ bool QThread::wait( unsigned long time ) return (ret == 0); } +/*! + Returns a pointer to the currently executing QThread. If the + current thread was not started using the QThread API, this + function returns zero. + + Note that QApplication creates a QThread object to represent the + main thread; calling this function from main() after creating + QApplication will return a valid pointer. +*/ +QThread *QThread::currentThreadObject() +{ + pthread_once(&storage_key_once, create_storage_key); + return reinterpret_cast<QThread *>(pthread_getspecific(storage_key)); +} + #endif // QT_THREAD_SUPPORT diff --git a/src/kernel/qwidget.cpp b/src/kernel/qwidget.cpp index 704681d..5ad69a0 100644 --- a/src/kernel/qwidget.cpp +++ b/src/kernel/qwidget.cpp @@ -56,6 +56,9 @@ #include "qstyle.h" #include "qmetaobject.h" #include "qguardedptr.h" +#if defined(QT_THREAD_SUPPORT) +#include "qthread.h" +#endif #if defined(QT_ACCESSIBILITY_SUPPORT) #include "qaccessible.h" #endif @@ -887,6 +890,12 @@ QWidget::QWidget( QWidget *parent, const char *name, WFlags f, NFlags n ) } #endif +#if defined(QT_THREAD_SUPPORT) && defined(QT_CHECK_STATE) + if (QThread::currentThreadObject() != QApplication::guiThread()) { + qFatal( "QWidget: Cannot create a QWidget outside of the main GUI thread" ); + } +#endif + fstrut_dirty = 1; isWidget = TRUE; // is a widget |