diff --git a/docs/changes.txt b/docs/changes.txt index bc90483f12..720a221077 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -166,6 +166,8 @@ wxGTK: - Fix wxNotebook::GetPage{Text,Image}() when they were called from the page change event handler for the first added page (Mikkel S). - Fixed wxBitmapButton to use focus and hover bitmaps correctly. +- Fixed race condition which could cause idle processing to stop without + processing all pending events. wxMac: diff --git a/src/gtk/app.cpp b/src/gtk/app.cpp index 444706b32f..b2fbaa035f 100644 --- a/src/gtk/app.cpp +++ b/src/gtk/app.cpp @@ -174,59 +174,63 @@ static gint wxapp_idle_callback( gpointer WXUNUSED(data) ) if (!wxTheApp) return false; - bool moreIdles = false; - -#ifdef __WXDEBUG__ - // don't generate the idle events while the assert modal dialog is shown, - // this matches the behavior of wxMSW - if (!wxTheApp->IsInAssert()) -#endif // __WXDEBUG__ - { - guint idleID_save; - { - // Allow another idle source to be added while this one is busy. - // Needed if an idle event handler runs a new event loop, - // for example by showing a dialog. -#if wxUSE_THREADS - wxMutexLocker lock(gs_idleTagsMutex); -#endif - idleID_save = wxTheApp->m_idleTag; - wxTheApp->m_idleTag = 0; - g_isIdle = true; - wxAddEmissionHook(); - } - - // When getting called from GDK's time-out handler - // we are no longer within GDK's grab on the GUI - // thread so we must lock it here ourselves. - gdk_threads_enter(); - - // Send idle event to all who request them as long as - // no events have popped up in the event queue. - do { - moreIdles = wxTheApp->ProcessIdle(); - } while (moreIdles && gtk_events_pending() == 0); - - // Release lock again - gdk_threads_leave(); - - { - // If another idle source was added, remove it -#if wxUSE_THREADS - wxMutexLocker lock(gs_idleTagsMutex); -#endif - if (wxTheApp->m_idleTag != 0) - g_source_remove(wxTheApp->m_idleTag); - wxTheApp->m_idleTag = idleID_save; - g_isIdle = false; - } - } - - if (!moreIdles) + guint idleID_save; { + // Allow another idle source to be added while this one is busy. + // Needed if an idle event handler runs a new event loop, + // for example by showing a dialog. #if wxUSE_THREADS wxMutexLocker lock(gs_idleTagsMutex); #endif + idleID_save = wxTheApp->m_idleTag; + wxTheApp->m_idleTag = 0; + g_isIdle = true; + wxAddEmissionHook(); + } +#ifdef __WXDEBUG__ + // don't generate the idle events while the assert modal dialog is shown, + // this matches the behavior of wxMSW + if (wxTheApp->IsInAssert()) + return false; +#endif // __WXDEBUG__ + + // When getting called from GDK's time-out handler + // we are no longer within GDK's grab on the GUI + // thread so we must lock it here ourselves. + gdk_threads_enter(); + + // Send idle event to all who request them as long as + // no events have popped up in the event queue. + bool moreIdles; + do { + moreIdles = wxTheApp->ProcessIdle(); + } while (moreIdles && gtk_events_pending() == 0); + + // Release lock again + gdk_threads_leave(); + +#if wxUSE_THREADS + wxMutexLocker lock(gs_idleTagsMutex); +#endif + // If another idle source was added, remove it + if (wxTheApp->m_idleTag != 0) + g_source_remove(wxTheApp->m_idleTag); + wxTheApp->m_idleTag = idleID_save; + g_isIdle = false; + +#if wxUSE_THREADS + if (wxPendingEventsLocker) + wxPendingEventsLocker->Enter(); +#endif + // Pending events can be added asynchronously, + // need to keep idle source if any have appeared + moreIdles = moreIdles || (wxPendingEvents && !wxPendingEvents->IsEmpty()); +#if wxUSE_THREADS + if (wxPendingEventsLocker) + wxPendingEventsLocker->Leave(); +#endif + if (!moreIdles) + { // Indicate that we are now in idle mode and event handlers // will have to reinstall the idle handler again. g_isIdle = true;