From c69c3400cbccaa8273918a68a749ce98a2340290 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Wed, 1 May 2013 15:08:14 -0500 Subject: [PATCH] Use glib thread initialization functions when glib main loop is in use This resolves Bug 1484 --- configure | 9 +--- src/kernel/qeventloop.cpp | 3 +- src/kernel/qeventloop_x11_glib.cpp | 85 +++++++++++++++--------------- src/kernel/qthread_unix.cpp | 34 ++++++++++-- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/configure b/configure index 0c34ea4..6032cc7 100755 --- a/configure +++ b/configure @@ -734,9 +734,6 @@ while [ "$#" -gt 0 ]; do glibmainloop) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_GLIBMAINLOOP="$VAL" - if [ "$VAL" = "yes" ]; then - echo "WARNING: glib main loop support is ***incomplete*** and will cause problems with threaded applications and/or those using non-standard event loops!" - fi else UNKNOWN_OPT=yes fi @@ -2644,13 +2641,11 @@ if [ "$CFG_GLIBMAINLOOP" = "no" ]; then elif [ "$CFG_GLIBMAINLOOP" = "yes" ]; then QMAKE_CONFIG="$QMAKE_CONFIG glibmainloop" - GLIB_CFLAGS="`pkg-config --cflags glib-2.0`" + GLIB_CFLAGS="`pkg-config --cflags glib-2.0` `pkg-config --cflags gthread-2.0`" QMAKE_VARS="$QMAKE_VARS \"QMAKE_CFLAGS_GLIB=$GLIB_CFLAGS\"" - GLIB_LINKFLAG="`pkg-config --libs glib-2.0`" + GLIB_LINKFLAG="`pkg-config --libs glib-2.0` `pkg-config --libs gthread-2.0`" QMAKE_VARS="$QMAKE_VARS \"QMAKE_LIBS_GLIB=$GLIB_LINKFLAG\"" - - fi if [ "$CFG_GIF" = "no" ]; then QMAKE_CONFIG="$QMAKE_CONFIG no-gif" diff --git a/src/kernel/qeventloop.cpp b/src/kernel/qeventloop.cpp index 5eadb9e..1285098 100644 --- a/src/kernel/qeventloop.cpp +++ b/src/kernel/qeventloop.cpp @@ -223,8 +223,9 @@ int QEventLoop::enterLoop() d->shortcut = FALSE; d->looplevel++; - while ( ! d->exitloop ) + while ( ! d->exitloop ) { processEvents( AllEvents | WaitForMore ); + } d->looplevel--; // restore the exitloop state, but if quitnow is TRUE, we need to keep diff --git a/src/kernel/qeventloop_x11_glib.cpp b/src/kernel/qeventloop_x11_glib.cpp index 5226aa9..23162c7 100644 --- a/src/kernel/qeventloop_x11_glib.cpp +++ b/src/kernel/qeventloop_x11_glib.cpp @@ -54,6 +54,7 @@ #include #include +#include // #define DEBUG_QT_GLIBMAINLOOP 1 @@ -109,7 +110,7 @@ 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 ) { Q_UNUSED(callback); Q_UNUSED(user_data); @@ -195,7 +196,7 @@ void qt_remove_postselect_handler( VFPTR handler ) void QEventLoop::init() { // initialize ProcessEventFlags (all events & wait for more) - d->pev_flags = AllEvents | WaitForMore; + d->pev_flags = AllEvents | WaitForMore; // initialize the common parts of the event loop if (pipe( d->thread_pipe ) < 0) { @@ -239,13 +240,12 @@ void QEventLoop::init() // poll thread-pipe d->threadPipe_gPollFD.fd = d->thread_pipe[0]; d->threadPipe_gPollFD.events = G_IO_IN | G_IO_HUP | G_IO_ERR; - + g_source_add_poll(d->gSource, &d->threadPipe_gPollFD); - + #ifdef DEBUG_QT_GLIBMAINLOOP printf("inside init(2)\n"); #endif - } void QEventLoop::cleanup() @@ -266,25 +266,24 @@ void QEventLoop::cleanup() bool QEventLoop::processEvents( ProcessEventsFlags flags ) { - #ifdef DEBUG_QT_GLIBMAINLOOP - printf("inside processEvents(1) looplevel=%d\n", d->looplevel ); + printf("inside processEvents(1) looplevel=%d eventloop=%p d->ctx=%p this=%p\n", d->looplevel, QApplication::eventLoop(), d->ctx, this ); fflush(stdout); #endif - + ProcessEventsFlags save_flags; int rval; save_flags = d->pev_flags; - + d->pev_flags = flags; - + rval = g_main_context_iteration(d->ctx, 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 - + printf("inside processEvents(2) looplevel=%d eventloop=%p d->ctx=%p this=%p rval=%d\n", d->looplevel, QApplication::eventLoop(), d->ctx, this, rval ); fflush(stdout); +#endif + return rval; // were events processed? } @@ -312,9 +311,9 @@ bool QEventLoop::processX11Events() if ( d->shortcut ) { return FALSE; } - + XNextEvent( QPaintDevice::x11AppDisplay(), &event ); - + if ( flags & ExcludeUserInput ) { switch ( event.type ) { case ButtonPress: @@ -325,31 +324,31 @@ bool QEventLoop::processX11Events() 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; @@ -358,13 +357,13 @@ bool QEventLoop::processX11Events() } } } - + 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 ) ) { @@ -372,27 +371,27 @@ bool QEventLoop::processX11Events() } return FALSE; } - - + + bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) { Q_UNUSED(gs); #ifdef DEBUG_QT_GLIBMAINLOOP printf("inside gsourcePrepare(1)\n"); -#endif +#endif ProcessEventsFlags flags = d->pev_flags; - + #if defined(QT_THREAD_SUPPORT) 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; @@ -406,13 +405,13 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) tm->tv_usec = 0; } } - + // include or exclude SocketNotifiers (by setting or cleaning poll events) if ( ! ( flags & ExcludeSocketNotifiers ) ) { QPtrListIterator it( d->sn_list ); QSockNotGPollFD *sn; while ( (sn=it.current()) ) { - ++it; + ++it; sn->gPollFD.events = sn->events; // restore poll events } } @@ -420,7 +419,7 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) QPtrListIterator it( d->sn_list ); QSockNotGPollFD *sn; while ( (sn=it.current()) ) { - ++it; + ++it; sn->gPollFD.events = 0; // delete poll events } } @@ -428,7 +427,7 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) #ifdef DEBUG_QT_GLIBMAINLOOP printf("inside gsourcePrepare(2) canwait=%d\n", canWait); #endif - + if ( canWait ) { emit aboutToBlock(); } @@ -442,7 +441,7 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) #ifdef DEBUG_QT_GLIBMAINLOOP printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait); #endif - + // do we have to dispatch events? if (hasPendingEvents()) { *timeout = 0; // no time to stay in poll @@ -451,7 +450,7 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) #endif return FALSE; } - + // stay in poll until something happens? if (!tm) { // fixme *timeout = -1; // wait forever @@ -460,10 +459,10 @@ bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) #endif 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 @@ -519,7 +518,7 @@ bool QEventLoop::gsourceCheck(GSource *gs) { #ifdef DEBUG_QT_GLIBMAINLOOP printf("inside gsourceCheck(2) qtwaittimer!\n"); #endif - + return TRUE; } @@ -553,10 +552,10 @@ bool QEventLoop::gsourceDispatch(GSource *gs) { // 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) { @@ -666,4 +665,4 @@ void QEventLoop::setSingleToolkitEventHandling(bool enabled) { d->ctx_is_default = true; } } -} \ No newline at end of file +} diff --git a/src/kernel/qthread_unix.cpp b/src/kernel/qthread_unix.cpp index 52b070e..6005c42 100644 --- a/src/kernel/qthread_unix.cpp +++ b/src/kernel/qthread_unix.cpp @@ -51,10 +51,12 @@ typedef pthread_mutex_t Q_MUTEX_T; #include #include +#if defined(QT_USE_GLIBMAINLOOP) +#include +#endif // QT_USE_GLIBMAINLOOP static QMutexPool *qt_thread_mutexpool = 0; - #if defined(Q_C_CALLBACKS) extern "C" { #endif @@ -121,6 +123,11 @@ void *QThreadInstance::start( void *_arg ) { void **arg = (void **) _arg; +#if defined(QT_USE_GLIBMAINLOOP) + // This is the first time we have access to the native pthread ID of this newly created thread + ((QThreadInstance*)arg[1])->thread_id = pthread_self(); +#endif // QT_USE_GLIBMAINLOOP + setCurrentThread( (QThread *) arg[0] ); pthread_cleanup_push( QThreadInstance::finish, arg[1] ); @@ -390,6 +397,20 @@ void QThread::start(Priority priority) d->args[0] = this; d->args[1] = d; +#if defined(QT_USE_GLIBMAINLOOP) + // Legacy glib versions require this threading system initialization call + g_thread_init(NULL); + + GThread* glib_thread_handle = g_thread_create((GThreadFunc)QThreadInstance::start, d->args, false, NULL); + if (glib_thread_handle) { + ret = 0; + } + else { + ret = -1; + } + // The correct thread_id is set in QThreadInstance::start using the value of d->args[1] + d->thread_id = NULL; +#else // QT_USE_GLIBMAINLOOP ret = pthread_create( &d->thread_id, &attr, (QtThreadCallback)QThreadInstance::start, d->args ); #if defined (Q_OS_HPUX) if (ret == EPERM) { @@ -398,6 +419,7 @@ void QThread::start(Priority priority) } #endif pthread_attr_destroy( &attr ); +#endif // QT_USE_GLIBMAINLOOP if ( ret ) { #ifdef QT_CHECK_STATE @@ -444,8 +466,9 @@ bool QThread::wait( unsigned long time ) return FALSE; } - if ( d->finished || ! d->running ) + if ( d->finished || ! d->running ) { return TRUE; + } int ret; if (time != ULONG_MAX) { @@ -458,12 +481,15 @@ bool QThread::wait( unsigned long time ) ti.tv_nsec %= 1000000000; ret = pthread_cond_timedwait(&d->thread_done, &locker.mutex()->d->handle, &ti); - } else + } + else { ret = pthread_cond_wait(&d->thread_done, &locker.mutex()->d->handle); + } #ifdef QT_CHECK_RANGE - if (ret && ret != ETIMEDOUT) + if (ret && ret != ETIMEDOUT) { qWarning("Wait condition wait failure: %s",strerror(ret)); + } #endif return (ret == 0);