mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1185639 - Allow deferred message processing to happen between consecutive IPC message dispatches. r=jimm
This commit is contained in:
parent
d0be04cef9
commit
f56584d2b0
@ -853,6 +853,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
|||||||
|
|
||||||
#ifdef OS_WIN
|
#ifdef OS_WIN
|
||||||
SyncStackFrame frame(this, false);
|
SyncStackFrame frame(this, false);
|
||||||
|
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CxxStackFrame f(*this, OUT_MESSAGE, msg);
|
CxxStackFrame f(*this, OUT_MESSAGE, msg);
|
||||||
@ -994,6 +995,7 @@ MessageChannel::Call(Message* aMsg, Message* aReply)
|
|||||||
|
|
||||||
#ifdef OS_WIN
|
#ifdef OS_WIN
|
||||||
SyncStackFrame frame(this, true);
|
SyncStackFrame frame(this, true);
|
||||||
|
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This must come before MonitorAutoLock, as its destructor acquires the
|
// This must come before MonitorAutoLock, as its destructor acquires the
|
||||||
@ -1032,6 +1034,12 @@ MessageChannel::Call(Message* aMsg, Message* aReply)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OS_WIN
|
||||||
|
/* We should pump messages at this point to ensure that the IPC peer
|
||||||
|
does not become deadlocked on a pending inter-thread SendMessage() */
|
||||||
|
neuteredRgn.PumpOnce();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Now might be the time to process a message deferred because of race
|
// Now might be the time to process a message deferred because of race
|
||||||
// resolution.
|
// resolution.
|
||||||
MaybeUndeferIncall();
|
MaybeUndeferIncall();
|
||||||
@ -1148,6 +1156,7 @@ MessageChannel::WaitForIncomingMessage()
|
|||||||
{
|
{
|
||||||
#ifdef OS_WIN
|
#ifdef OS_WIN
|
||||||
SyncStackFrame frame(this, true);
|
SyncStackFrame frame(this, true);
|
||||||
|
NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{ // Scope for lock
|
{ // Scope for lock
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
#include "mozilla/Monitor.h"
|
#include "mozilla/Monitor.h"
|
||||||
#include "mozilla/Vector.h"
|
#include "mozilla/Vector.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
#include "mozilla/ipc/Neutering.h"
|
||||||
|
#endif // defined(OS_WIN)
|
||||||
#include "mozilla/ipc/Transport.h"
|
#include "mozilla/ipc/Transport.h"
|
||||||
#include "MessageLink.h"
|
#include "MessageLink.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
64
ipc/glue/Neutering.h
Normal file
64
ipc/glue/Neutering.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_ipc_Neutering_h
|
||||||
|
#define mozilla_ipc_Neutering_h
|
||||||
|
|
||||||
|
#include "mozilla/GuardObjects.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This header declares RAII wrappers for Window neutering. See
|
||||||
|
* WindowsMessageLoop.cpp for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is a RAII wrapper around Window neutering. As long as a
|
||||||
|
* NeuteredWindowRegion object is instantiated, Win32 windows belonging to the
|
||||||
|
* current thread will be neutered. It is safe to nest multiple instances of
|
||||||
|
* this class.
|
||||||
|
*/
|
||||||
|
class MOZ_STACK_CLASS NeuteredWindowRegion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||||
|
~NeuteredWindowRegion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function clears any backlog of nonqueued messages that are pending for
|
||||||
|
* the current thread.
|
||||||
|
*/
|
||||||
|
void PumpOnce();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
bool mNeuteredByThis;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class
|
||||||
|
* that is to be instantiated within a NeuteredWindowRegion, thus temporarily
|
||||||
|
* disabling neutering for the remainder of its enclosing block.
|
||||||
|
* @see NeuteredWindowRegion
|
||||||
|
*/
|
||||||
|
class MOZ_STACK_CLASS DeneuteredWindowRegion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
||||||
|
~DeneuteredWindowRegion();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
bool mReneuter;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_ipc_Neutering_h
|
||||||
|
|
@ -8,6 +8,7 @@
|
|||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
|
|
||||||
#include "WindowsMessageLoop.h"
|
#include "WindowsMessageLoop.h"
|
||||||
|
#include "Neutering.h"
|
||||||
#include "MessageChannel.h"
|
#include "MessageChannel.h"
|
||||||
|
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
@ -862,6 +863,81 @@ IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
|
|||||||
(aTimeout <= (PR_IntervalNow() - aStart));
|
(aTimeout <= (PR_IntervalNow() - aStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HHOOK gWindowHook;
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
StartNeutering()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(gUIThreadId);
|
||||||
|
MOZ_ASSERT(!gWindowHook);
|
||||||
|
NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
|
||||||
|
"Shouldn't be pumping already!");
|
||||||
|
MessageChannel::SetIsPumpingMessages(true);
|
||||||
|
gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
|
||||||
|
nullptr, gUIThreadId);
|
||||||
|
NS_ASSERTION(gWindowHook, "Failed to set hook!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
StopNeutering()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(MessageChannel::IsPumpingMessages());
|
||||||
|
::UnhookWindowsHookEx(gWindowHook);
|
||||||
|
gWindowHook = NULL;
|
||||||
|
::UnhookNeuteredWindows();
|
||||||
|
// Before returning we need to set a hook to run any deferred messages that
|
||||||
|
// we received during the IPC call. The hook will unset itself as soon as
|
||||||
|
// someone else calls GetMessage, PeekMessage, or runs code that generates
|
||||||
|
// a "nonqueued" message.
|
||||||
|
::ScheduleDeferredMessageRun();
|
||||||
|
MessageChannel::SetIsPumpingMessages(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||||
|
: mNeuteredByThis(!gWindowHook)
|
||||||
|
{
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
if (aDoNeuter && mNeuteredByThis) {
|
||||||
|
StartNeutering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NeuteredWindowRegion::~NeuteredWindowRegion()
|
||||||
|
{
|
||||||
|
if (gWindowHook && mNeuteredByThis) {
|
||||||
|
StopNeutering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NeuteredWindowRegion::PumpOnce()
|
||||||
|
{
|
||||||
|
MSG msg = {0};
|
||||||
|
// Pump any COM messages so that we don't hang due to STA marshaling.
|
||||||
|
if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
|
||||||
|
::TranslateMessage(&msg);
|
||||||
|
::DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
// Expunge any nonqueued messages on the current thread.
|
||||||
|
::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||||
|
: mReneuter(gWindowHook != NULL)
|
||||||
|
{
|
||||||
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
if (mReneuter) {
|
||||||
|
StopNeutering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeneuteredWindowRegion::~DeneuteredWindowRegion()
|
||||||
|
{
|
||||||
|
if (mReneuter) {
|
||||||
|
StartNeutering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MessageChannel::WaitForSyncNotify()
|
MessageChannel::WaitForSyncNotify()
|
||||||
{
|
{
|
||||||
@ -916,15 +992,6 @@ MessageChannel::WaitForSyncNotify()
|
|||||||
NS_ASSERTION(timerId, "SetTimer failed!");
|
NS_ASSERTION(timerId, "SetTimer failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup deferred processing of native events while we wait for a response.
|
|
||||||
NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
|
|
||||||
"Shouldn't be pumping already!");
|
|
||||||
|
|
||||||
MessageChannel::SetIsPumpingMessages(true);
|
|
||||||
HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
|
|
||||||
nullptr, gUIThreadId);
|
|
||||||
NS_ASSERTION(windowHook, "Failed to set hook!");
|
|
||||||
|
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
MSG msg = { 0 };
|
MSG msg = { 0 };
|
||||||
@ -998,25 +1065,11 @@ MessageChannel::WaitForSyncNotify()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unhook the neutered window procedure hook.
|
|
||||||
UnhookWindowsHookEx(windowHook);
|
|
||||||
|
|
||||||
// Unhook any neutered windows procedures so messages can be delivered
|
|
||||||
// normally.
|
|
||||||
UnhookNeuteredWindows();
|
|
||||||
|
|
||||||
// Before returning we need to set a hook to run any deferred messages that
|
|
||||||
// we received during the IPC call. The hook will unset itself as soon as
|
|
||||||
// someone else calls GetMessage, PeekMessage, or runs code that generates
|
|
||||||
// a "nonqueued" message.
|
|
||||||
ScheduleDeferredMessageRun();
|
|
||||||
|
|
||||||
if (timerId) {
|
if (timerId) {
|
||||||
KillTimer(nullptr, timerId);
|
KillTimer(nullptr, timerId);
|
||||||
|
timerId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageChannel::SetIsPumpingMessages(false);
|
|
||||||
|
|
||||||
return WaitResponse(timedout);
|
return WaitResponse(timedout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1050,57 +1103,29 @@ MessageChannel::WaitForInterruptNotify()
|
|||||||
UINT_PTR timerId = 0;
|
UINT_PTR timerId = 0;
|
||||||
TimeoutData timeoutData = { 0 };
|
TimeoutData timeoutData = { 0 };
|
||||||
|
|
||||||
// windowHook is used as a flag variable for the loop below: if it is set
|
// gWindowHook is used as a flag variable for the loop below: if it is set
|
||||||
// and we start to spin a nested event loop, we need to clear the hook and
|
// and we start to spin a nested event loop, we need to clear the hook and
|
||||||
// process deferred/pending messages.
|
// process deferred/pending messages.
|
||||||
// If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
|
|
||||||
HHOOK windowHook = nullptr;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
|
NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(),
|
||||||
"windowHook out of sync with reality");
|
"gWindowHook out of sync with reality");
|
||||||
|
|
||||||
if (mTopFrame->mSpinNestedEvents) {
|
if (mTopFrame->mSpinNestedEvents) {
|
||||||
if (windowHook) {
|
if (gWindowHook && timerId) {
|
||||||
UnhookWindowsHookEx(windowHook);
|
|
||||||
windowHook = nullptr;
|
|
||||||
|
|
||||||
if (timerId) {
|
|
||||||
KillTimer(nullptr, timerId);
|
KillTimer(nullptr, timerId);
|
||||||
timerId = 0;
|
timerId = 0;
|
||||||
}
|
}
|
||||||
|
DeneuteredWindowRegion deneuteredRgn;
|
||||||
// Used by widget to assert on incoming native events
|
|
||||||
MessageChannel::SetIsPumpingMessages(false);
|
|
||||||
|
|
||||||
// Unhook any neutered windows procedures so messages can be delievered
|
|
||||||
// normally.
|
|
||||||
UnhookNeuteredWindows();
|
|
||||||
|
|
||||||
// Send all deferred "nonqueued" message to the intended receiver.
|
|
||||||
// We're dropping into SpinInternalEventLoop so we should be fairly
|
|
||||||
// certain these will get delivered soohn.
|
|
||||||
ScheduleDeferredMessageRun();
|
|
||||||
}
|
|
||||||
SpinInternalEventLoop();
|
SpinInternalEventLoop();
|
||||||
ResetEvent(mEvent);
|
ResetEvent(mEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!windowHook) {
|
if (mTimeoutMs != kNoTimeout && !timerId) {
|
||||||
MessageChannel::SetIsPumpingMessages(true);
|
|
||||||
windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
|
|
||||||
nullptr, gUIThreadId);
|
|
||||||
NS_ASSERTION(windowHook, "Failed to set hook!");
|
|
||||||
|
|
||||||
NS_ASSERTION(!timerId, "Timer already initialized?");
|
|
||||||
|
|
||||||
if (mTimeoutMs != kNoTimeout) {
|
|
||||||
InitTimeoutData(&timeoutData, mTimeoutMs);
|
InitTimeoutData(&timeoutData, mTimeoutMs);
|
||||||
timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
|
timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
|
||||||
NS_ASSERTION(timerId, "SetTimer failed!");
|
NS_ASSERTION(timerId, "SetTimer failed!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MSG msg = { 0 };
|
MSG msg = { 0 };
|
||||||
|
|
||||||
@ -1151,26 +1176,10 @@ MessageChannel::WaitForInterruptNotify()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (windowHook) {
|
|
||||||
// Unhook the neutered window procedure hook.
|
|
||||||
UnhookWindowsHookEx(windowHook);
|
|
||||||
|
|
||||||
// Unhook any neutered windows procedures so messages can be delivered
|
|
||||||
// normally.
|
|
||||||
UnhookNeuteredWindows();
|
|
||||||
|
|
||||||
// Before returning we need to set a hook to run any deferred messages that
|
|
||||||
// we received during the IPC call. The hook will unset itself as soon as
|
|
||||||
// someone else calls GetMessage, PeekMessage, or runs code that generates
|
|
||||||
// a "nonqueued" message.
|
|
||||||
ScheduleDeferredMessageRun();
|
|
||||||
|
|
||||||
if (timerId) {
|
if (timerId) {
|
||||||
KillTimer(nullptr, timerId);
|
KillTimer(nullptr, timerId);
|
||||||
|
timerId = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MessageChannel::SetIsPumpingMessages(false);
|
|
||||||
|
|
||||||
return WaitResponse(timedout);
|
return WaitResponse(timedout);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ EXPORTS.mozilla.ipc += [
|
|||||||
'IOThreadChild.h',
|
'IOThreadChild.h',
|
||||||
'MessageChannel.h',
|
'MessageChannel.h',
|
||||||
'MessageLink.h',
|
'MessageLink.h',
|
||||||
|
'Neutering.h',
|
||||||
'ProcessChild.h',
|
'ProcessChild.h',
|
||||||
'ProtocolUtils.h',
|
'ProtocolUtils.h',
|
||||||
'ScopedXREEmbed.h',
|
'ScopedXREEmbed.h',
|
||||||
|
Loading…
Reference in New Issue
Block a user