Bug 575515 - Fix for deadlocks in windows modal loop message processing under heavy js load. r=matspal, a=betaN

This commit is contained in:
Jim Mathies 2010-11-29 10:25:16 -06:00
Родитель 934c084783
Коммит b91f05cdaa
4 изменённых файлов: 74 добавлений и 8 удалений

Просмотреть файл

@ -22,6 +22,7 @@
* Contributor(s): * Contributor(s):
* Michael Lowe <michael.lowe@bigfoot.com> * Michael Lowe <michael.lowe@bigfoot.com>
* Darin Fisher <darin@meer.net> * Darin Fisher <darin@meer.net>
* Jim Mathies <jmathies@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -48,6 +49,9 @@
#include <windows.h> #include <windows.h>
#include <tlhelp32.h> #include <tlhelp32.h>
const PRUnichar* kAppShellEventId = L"nsAppShell:EventID";
const PRUnichar* kTaskbarButtonEventId = L"TaskbarButtonCreated";
#ifdef WINCE #ifdef WINCE
BOOL WaitMessage(VOID) BOOL WaitMessage(VOID)
{ {
@ -140,10 +144,10 @@ nsAppShell::Init()
#endif #endif
if (!sMsgId) if (!sMsgId)
sMsgId = RegisterWindowMessageW(L"nsAppShell:EventID"); sMsgId = RegisterWindowMessageW(kAppShellEventId);
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(L"TaskbarButtonCreated"); sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message"); NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
// Global app registration id for Win7 and up. See // Global app registration id for Win7 and up. See
@ -176,7 +180,6 @@ nsAppShell::Init()
return nsBaseAppShell::Init(); return nsBaseAppShell::Init();
} }
/** /**
* This is some temporary code to keep track of where in memory dlls are * This is some temporary code to keep track of where in memory dlls are
* loaded. This is useful in case someone calls into a dll that has been * loaded. This is useful in case someone calls into a dll that has been
@ -262,11 +265,45 @@ nsAppShell::Run(void)
#endif #endif
void
nsAppShell::DoProcessMoreGeckoEvents()
{
// Called by nsBaseAppShell's NativeEventCallback() after it has finished
// processing pending gecko events and there are still gecko events pending
// for the thread. (This can happen if NS_ProcessPendingEvents reached it's
// starvation timeout limit.) The default behavior in nsBaseAppShell is to
// call ScheduleNativeEventCallback to post a follow up native event callback
// message. This triggers an additional call to NativeEventCallback for more
// gecko event processing.
// There's a deadlock risk here with certain internal Windows modal loops. In
// our dispatch code, we prioritize messages so that input is handled first.
// However Windows modal dispatch loops often prioritize posted messages. If
// we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents
// takes longer than the timer duration, NS_HasPendingEvents(thread) will
// always be true. ScheduleNativeEventCallback will be called on every
// NativeEventCallback callback, and in a Windows modal dispatch loop, the
// callback message will be processed first -> input gets starved, dead lock.
// To avoid, don't post native callback messages from NativeEventCallback
// when we're in a modal loop. This gets us back into the Windows modal
// dispatch loop dispatching input messages. Once we drop out of the modal
// loop, we use mNativeCallbackPending to fire off a final NativeEventCallback
// if we need it, which insures NS_ProcessPendingEvents gets called and all
// gecko events get processed.
if (mEventloopNestingLevel < 2) {
OnDispatchedEvent(nsnull);
mNativeCallbackPending = PR_FALSE;
} else {
mNativeCallbackPending = PR_TRUE;
}
}
void void
nsAppShell::ScheduleNativeEventCallback() nsAppShell::ScheduleNativeEventCallback()
{ {
// post a message to the native event queue... // Post a message to the hidden message window
NS_ADDREF_THIS(); NS_ADDREF_THIS(); // will be released when the event is processed
::PostMessage(mEventWnd, sMsgId, 0, reinterpret_cast<LPARAM>(this)); ::PostMessage(mEventWnd, sMsgId, 0, reinterpret_cast<LPARAM>(this));
} }
@ -303,5 +340,10 @@ nsAppShell::ProcessNextNativeEvent(PRBool mayWait)
} }
} while (!gotMessage && mayWait); } while (!gotMessage && mayWait);
// See DoProcessNextNativeEvent, mEventloopNestingLevel will be
// one when a modal loop unwinds.
if (mNativeCallbackPending && mEventloopNestingLevel == 1)
DoProcessMoreGeckoEvents();
return gotMessage; return gotMessage;
} }

Просмотреть файл

@ -47,9 +47,13 @@
class nsAppShell : public nsBaseAppShell class nsAppShell : public nsBaseAppShell
{ {
public: public:
nsAppShell() : mEventWnd(NULL) {} nsAppShell() :
mEventWnd(NULL),
mNativeCallbackPending(PR_FALSE)
{}
nsresult Init(); nsresult Init();
void DoProcessMoreGeckoEvents();
#if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
static UINT GetTaskbarButtonCreatedMessage(); static UINT GetTaskbarButtonCreatedMessage();
@ -67,6 +71,7 @@ protected:
protected: protected:
HWND mEventWnd; HWND mEventWnd;
PRBool mNativeCallbackPending;
}; };
#endif // nsAppShell_h__ #endif // nsAppShell_h__

Просмотреть файл

@ -93,6 +93,7 @@ nsBaseAppShell::Init()
return NS_OK; return NS_OK;
} }
// Called by nsAppShell's native event callback
void void
nsBaseAppShell::NativeEventCallback() nsBaseAppShell::NativeEventCallback()
{ {
@ -136,11 +137,21 @@ nsBaseAppShell::NativeEventCallback()
// Continue processing pending events later (we don't want to starve the // Continue processing pending events later (we don't want to starve the
// embedders event loop). // embedders event loop).
if (NS_HasPendingEvents(thread)) if (NS_HasPendingEvents(thread))
OnDispatchedEvent(nsnull); DoProcessMoreGeckoEvents();
--mEventloopNestingLevel; --mEventloopNestingLevel;
} }
// Note, this is currently overidden on windows, see comments in nsAppShell for
// details.
void
nsBaseAppShell::DoProcessMoreGeckoEvents()
{
OnDispatchedEvent(nsnull);
}
// Main thread via OnProcessNextEvent below
PRBool PRBool
nsBaseAppShell::DoProcessNextNativeEvent(PRBool mayWait) nsBaseAppShell::DoProcessNextNativeEvent(PRBool mayWait)
{ {
@ -255,6 +266,7 @@ nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr)
if (lastVal == 1) if (lastVal == 1)
return NS_OK; return NS_OK;
// Returns on the main thread in NativeEventCallback above
ScheduleNativeEventCallback(); ScheduleNativeEventCallback();
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -75,6 +75,13 @@ protected:
*/ */
void NativeEventCallback(); void NativeEventCallback();
/**
* Make a decision as to whether or not NativeEventCallback will
* trigger gecko event processing when there are pending gecko
* events.
*/
virtual void DoProcessMoreGeckoEvents();
/** /**
* Implemented by subclasses. Invoke NativeEventCallback from a native * Implemented by subclasses. Invoke NativeEventCallback from a native
* event. This method may be called on any thread. * event. This method may be called on any thread.
@ -96,6 +103,7 @@ protected:
virtual PRBool ProcessNextNativeEvent(PRBool mayWait) = 0; virtual PRBool ProcessNextNativeEvent(PRBool mayWait) = 0;
PRInt32 mSuspendNativeCount; PRInt32 mSuspendNativeCount;
PRUint32 mEventloopNestingLevel;
private: private:
PRBool DoProcessNextNativeEvent(PRBool mayWait); PRBool DoProcessNextNativeEvent(PRBool mayWait);
@ -115,7 +123,6 @@ private:
PRBool *mBlockedWait; PRBool *mBlockedWait;
PRInt32 mFavorPerf; PRInt32 mFavorPerf;
PRInt32 mNativeEventPending; PRInt32 mNativeEventPending;
PRUint32 mEventloopNestingLevel;
PRIntervalTime mStarvationDelay; PRIntervalTime mStarvationDelay;
PRIntervalTime mSwitchTime; PRIntervalTime mSwitchTime;
PRIntervalTime mLastNativeEventTime; PRIntervalTime mLastNativeEventTime;