зеркало из https://github.com/mozilla/gecko-dev.git
Bug 545053: Implement IPC hang detection for windows. r=jimm
This commit is contained in:
Родитель
545241bd7c
Коммит
c38a2c6616
|
@ -40,10 +40,6 @@
|
|||
#ifndef ipc_glue_SyncChannel_h
|
||||
#define ipc_glue_SyncChannel_h 1
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "prinrval.h"
|
||||
|
||||
#include "mozilla/ipc/AsyncChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -155,10 +151,10 @@ protected:
|
|||
|
||||
static bool sIsPumpingMessages;
|
||||
|
||||
int32 mTimeoutMs;
|
||||
|
||||
private:
|
||||
bool EventOccurred();
|
||||
|
||||
int32 mTimeoutMs;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -517,6 +517,45 @@ Init()
|
|||
"Running on different threads!");
|
||||
}
|
||||
|
||||
// This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
|
||||
// value for GetTickCount(), which is something like 50 days). It uses the
|
||||
// cheapest (and least accurate) method supported by Windows 2000.
|
||||
|
||||
struct TimeoutData
|
||||
{
|
||||
DWORD startTicks;
|
||||
DWORD targetTicks;
|
||||
};
|
||||
|
||||
void
|
||||
InitTimeoutData(TimeoutData* aData,
|
||||
int32 aTimeoutMs)
|
||||
{
|
||||
aData->startTicks = GetTickCount();
|
||||
if (!aData->startTicks) {
|
||||
// How unlikely is this!
|
||||
aData->startTicks++;
|
||||
}
|
||||
aData->targetTicks = aData->startTicks + aTimeoutMs;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TimeoutHasExpired(const TimeoutData& aData)
|
||||
{
|
||||
if (!aData.startTicks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD now = GetTickCount();
|
||||
|
||||
if (aData.targetTicks < aData.startTicks) {
|
||||
// Overflow
|
||||
return now < aData.startTicks && now >= aData.targetTicks;
|
||||
}
|
||||
return now >= aData.targetTicks;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool
|
||||
|
@ -609,12 +648,26 @@ SyncChannel::WaitForNotify()
|
|||
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
bool retval = true;
|
||||
|
||||
if (++gEventLoopDepth == 1) {
|
||||
NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
|
||||
gNeuteredWindows = new nsAutoTArray<HWND, 20>();
|
||||
NS_ASSERTION(gNeuteredWindows, "Out of memory!");
|
||||
}
|
||||
|
||||
UINT_PTR timerId = NULL;
|
||||
TimeoutData timeoutData = { 0 };
|
||||
|
||||
if (mTimeoutMs != kNoTimeout) {
|
||||
InitTimeoutData(&timeoutData, mTimeoutMs);
|
||||
|
||||
// We only do this to ensure that we won't get stuck in
|
||||
// MsgWaitForMultipleObjects below.
|
||||
timerId = SetTimer(NULL, 0, mTimeoutMs, NULL);
|
||||
NS_ASSERTION(timerId, "SetTimer failed!");
|
||||
}
|
||||
|
||||
// Setup deferred processing of native events while we wait for a response.
|
||||
NS_ASSERTION(!SyncChannel::IsPumpingMessages(),
|
||||
"Shouldn't be pumping already!");
|
||||
|
@ -630,7 +683,7 @@ SyncChannel::WaitForNotify()
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!Connected()) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,6 +700,12 @@ SyncChannel::WaitForNotify()
|
|||
break;
|
||||
}
|
||||
|
||||
if (TimeoutHasExpired(timeoutData)) {
|
||||
// A timeout was specified and we've passed it. Break out.
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// The only way to know on which thread the message was delivered is to
|
||||
// use some logic on the return values of GetQueueStatus and PeekMessage.
|
||||
// PeekMessage will return false if there are no "queued" messages, but it
|
||||
|
@ -702,9 +761,13 @@ SyncChannel::WaitForNotify()
|
|||
// a "nonqueued" message.
|
||||
ScheduleDeferredMessageRun();
|
||||
|
||||
if (timerId) {
|
||||
KillTimer(NULL, timerId);
|
||||
}
|
||||
|
||||
SyncChannel::SetIsPumpingMessages(false);
|
||||
|
||||
return true;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -712,11 +775,18 @@ RPCChannel::WaitForNotify()
|
|||
{
|
||||
mMutex.AssertCurrentThreadOwns();
|
||||
|
||||
if (!StackDepth() && !mBlockedOnParent) {
|
||||
// There is currently no way to recover from this condition.
|
||||
NS_RUNTIMEABORT("StackDepth() is 0 in call to RPCChannel::WaitForNotify!");
|
||||
}
|
||||
|
||||
// Initialize global objects used in deferred messaging.
|
||||
Init();
|
||||
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
|
||||
bool retval = true;
|
||||
|
||||
// IsSpinLoopActive indicates modal UI is being displayed in a plugin. Drop
|
||||
// down into the spin loop until all modal loops end. If SpinInternalEventLoop
|
||||
// returns true, the out-call response we were waiting on arrived, or we
|
||||
|
@ -737,6 +807,18 @@ RPCChannel::WaitForNotify()
|
|||
NS_ASSERTION(gNeuteredWindows, "Out of memory!");
|
||||
}
|
||||
|
||||
UINT_PTR timerId = NULL;
|
||||
TimeoutData timeoutData = { 0 };
|
||||
|
||||
if (mTimeoutMs != kNoTimeout) {
|
||||
InitTimeoutData(&timeoutData, mTimeoutMs);
|
||||
|
||||
// We only do this to ensure that we won't get stuck in
|
||||
// MsgWaitForMultipleObjects below.
|
||||
timerId = SetTimer(NULL, 0, mTimeoutMs, NULL);
|
||||
NS_ASSERTION(timerId, "SetTimer failed!");
|
||||
}
|
||||
|
||||
// Setup deferred processing of native events while we wait for a response.
|
||||
NS_ASSERTION(!SyncChannel::IsPumpingMessages(),
|
||||
"Shouldn't be pumping already!");
|
||||
|
@ -752,8 +834,9 @@ RPCChannel::WaitForNotify()
|
|||
// Don't get wrapped up in here if the child connection dies.
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!Connected())
|
||||
if (!Connected()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, INFINITE,
|
||||
|
@ -763,6 +846,12 @@ RPCChannel::WaitForNotify()
|
|||
break;
|
||||
}
|
||||
|
||||
if (TimeoutHasExpired(timeoutData)) {
|
||||
// A timeout was specified and we've passed it. Break out.
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// See SyncChannel's WaitForNotify for details.
|
||||
bool haveSentMessagesPending =
|
||||
(HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
|
||||
|
@ -779,6 +868,11 @@ RPCChannel::WaitForNotify()
|
|||
UnhookWindowsHookEx(windowHook);
|
||||
windowHook = NULL;
|
||||
|
||||
if (timerId) {
|
||||
KillTimer(NULL, timerId);
|
||||
timerId = NULL;
|
||||
}
|
||||
|
||||
// Used by widget to assert on incoming native events.
|
||||
SyncChannel::SetIsPumpingMessages(false);
|
||||
|
||||
|
@ -832,9 +926,13 @@ RPCChannel::WaitForNotify()
|
|||
// a "nonqueued" message.
|
||||
ScheduleDeferredMessageRun();
|
||||
|
||||
if (timerId) {
|
||||
KillTimer(NULL, timerId);
|
||||
}
|
||||
|
||||
SyncChannel::SetIsPumpingMessages(false);
|
||||
|
||||
return true;
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Загрузка…
Ссылка в новой задаче