зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1267297 - Use AutoEntryScript for script activity bookkeeping instead of the request machinery. r=bholley
Differential Revision: https://phabricator.services.mozilla.com/D4085 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
56ca0b122a
Коммит
53f1370b5f
|
@ -662,8 +662,11 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aGlobalObject);
|
MOZ_ASSERT(aGlobalObject);
|
||||||
|
|
||||||
if (aIsMainThread && gRunToCompletionListeners > 0) {
|
if (aIsMainThread) {
|
||||||
mDocShellEntryMonitor.emplace(cx(), aReason);
|
if (gRunToCompletionListeners > 0) {
|
||||||
|
mDocShellEntryMonitor.emplace(cx(), aReason);
|
||||||
|
}
|
||||||
|
mScriptActivity.emplace(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,11 +818,12 @@ AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||||
: AutoJSAPI()
|
: mIsMainThread(NS_IsMainThread())
|
||||||
{
|
{
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||||
|
if (mIsMainThread) {
|
||||||
Init();
|
mScriptActivity.emplace(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -828,8 +832,10 @@ AutoSlowOperation::CheckForInterrupt()
|
||||||
// For now we support only main thread!
|
// For now we support only main thread!
|
||||||
if (mIsMainThread) {
|
if (mIsMainThread) {
|
||||||
// JS_CheckForInterrupt expects us to be in a realm.
|
// JS_CheckForInterrupt expects us to be in a realm.
|
||||||
JSAutoRealm ar(cx(), xpc::UnprivilegedJunkScope());
|
AutoJSAPI jsapi;
|
||||||
JS_CheckForInterrupt(cx());
|
if (jsapi.Init(xpc::UnprivilegedJunkScope())) {
|
||||||
|
JS_CheckForInterrupt(jsapi.cx());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "MainThreadUtils.h"
|
#include "MainThreadUtils.h"
|
||||||
#include "nsIGlobalObject.h"
|
#include "nsIGlobalObject.h"
|
||||||
#include "nsIPrincipal.h"
|
#include "nsIPrincipal.h"
|
||||||
|
#include "xpcpublic.h"
|
||||||
|
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
|
@ -383,6 +384,7 @@ private:
|
||||||
friend nsIPrincipal* GetWebIDLCallerPrincipal();
|
friend nsIPrincipal* GetWebIDLCallerPrincipal();
|
||||||
|
|
||||||
Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
|
Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
|
||||||
|
Maybe<xpc::AutoScriptActivity> mScriptActivity;
|
||||||
JS::AutoHideScriptedCaller mCallerOverride;
|
JS::AutoHideScriptedCaller mCallerOverride;
|
||||||
#ifdef MOZ_GECKO_PROFILER
|
#ifdef MOZ_GECKO_PROFILER
|
||||||
AutoProfilerLabel mAutoProfilerLabel;
|
AutoProfilerLabel mAutoProfilerLabel;
|
||||||
|
@ -455,17 +457,21 @@ private:
|
||||||
* Use AutoSlowOperation when native side calls many JS callbacks in a row
|
* Use AutoSlowOperation when native side calls many JS callbacks in a row
|
||||||
* and slow script dialog should be activated if too much time is spent going
|
* and slow script dialog should be activated if too much time is spent going
|
||||||
* through those callbacks.
|
* through those callbacks.
|
||||||
* AutoSlowOperation puts a JSAutoRequest on the stack so that we don't continue
|
* AutoSlowOperation puts an AutoScriptActivity on the stack so that we don't
|
||||||
* to reset the watchdog and CheckForInterrupt can be then used to check whether
|
* continue to reset the watchdog. CheckForInterrupt can then be used to check
|
||||||
* JS execution should be interrupted.
|
* whether JS execution should be interrupted.
|
||||||
|
* This class (including CheckForInterrupt) is a no-op when used off the main
|
||||||
|
* thread.
|
||||||
*/
|
*/
|
||||||
class MOZ_RAII AutoSlowOperation : public dom::AutoJSAPI
|
class MOZ_RAII AutoSlowOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
explicit AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
|
||||||
void CheckForInterrupt();
|
void CheckForInterrupt();
|
||||||
private:
|
private:
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
bool mIsMainThread;
|
||||||
|
Maybe<xpc::AutoScriptActivity> mScriptActivity;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -551,28 +551,49 @@ XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
|
||||||
mWatchdogManager->GetTimestamp(aCategory, lock);
|
mWatchdogManager->GetTimestamp(aCategory, lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
xpc::SimulateActivityCallback(bool aActive)
|
|
||||||
{
|
|
||||||
XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void
|
bool
|
||||||
XPCJSContext::ActivityCallback(void* arg, bool active)
|
XPCJSContext::RecordScriptActivity(bool aActive)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
XPCJSContext* xpccx = XPCJSContext::Get();
|
||||||
|
if (!xpccx) {
|
||||||
|
// mozilla::SpinEventLoopUntil may use AutoScriptActivity(false) after
|
||||||
|
// we destroyed the XPCJSContext.
|
||||||
|
MOZ_ASSERT(!aActive);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oldValue = xpccx->SetHasScriptActivity(aActive);
|
||||||
|
if (aActive == oldValue) {
|
||||||
|
// Nothing to do.
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Since the slow script dialog never activates if we are recording or
|
// Since the slow script dialog never activates if we are recording or
|
||||||
// replaying, don't record/replay JS activity notifications.
|
// replaying, don't record/replay JS activity notifications.
|
||||||
if (recordreplay::IsRecordingOrReplaying()) {
|
if (recordreplay::IsRecordingOrReplaying()) {
|
||||||
return;
|
return oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!active) {
|
if (!aActive) {
|
||||||
ProcessHangMonitor::ClearHang();
|
ProcessHangMonitor::ClearHang();
|
||||||
}
|
}
|
||||||
|
xpccx->mWatchdogManager->RecordContextActivity(xpccx, aActive);
|
||||||
|
|
||||||
XPCJSContext* self = static_cast<XPCJSContext*>(arg);
|
return oldValue;
|
||||||
self->mWatchdogManager->RecordContextActivity(self, active);
|
}
|
||||||
|
|
||||||
|
AutoScriptActivity::AutoScriptActivity(bool aActive)
|
||||||
|
: mActive(aActive)
|
||||||
|
, mOldValue(XPCJSContext::RecordScriptActivity(aActive))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoScriptActivity::~AutoScriptActivity()
|
||||||
|
{
|
||||||
|
MOZ_ALWAYS_TRUE(mActive == XPCJSContext::RecordScriptActivity(mOldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -890,8 +911,6 @@ XPCJSContext::~XPCJSContext()
|
||||||
Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
|
Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
js::SetActivityCallback(Context(), nullptr, nullptr);
|
|
||||||
|
|
||||||
// Clear any pending exception. It might be an XPCWrappedJS, and if we try
|
// Clear any pending exception. It might be an XPCWrappedJS, and if we try
|
||||||
// to destroy it later we will crash.
|
// to destroy it later we will crash.
|
||||||
SetPendingException(nullptr);
|
SetPendingException(nullptr);
|
||||||
|
@ -926,6 +945,7 @@ XPCJSContext::XPCJSContext()
|
||||||
mWatchdogManager(GetWatchdogManager()),
|
mWatchdogManager(GetWatchdogManager()),
|
||||||
mSlowScriptSecondHalf(false),
|
mSlowScriptSecondHalf(false),
|
||||||
mTimeoutAccumulated(false),
|
mTimeoutAccumulated(false),
|
||||||
|
mHasScriptActivity(false),
|
||||||
mPendingResult(NS_OK),
|
mPendingResult(NS_OK),
|
||||||
mActive(CONTEXT_INACTIVE),
|
mActive(CONTEXT_INACTIVE),
|
||||||
mLastStateChange(PR_Now())
|
mLastStateChange(PR_Now())
|
||||||
|
@ -1140,7 +1160,6 @@ XPCJSContext::Initialize(XPCJSContext* aPrimaryContext)
|
||||||
|
|
||||||
PROFILER_SET_JS_CONTEXT(cx);
|
PROFILER_SET_JS_CONTEXT(cx);
|
||||||
|
|
||||||
js::SetActivityCallback(cx, ActivityCallback, this);
|
|
||||||
JS_AddInterruptCallback(cx, InterruptCallback);
|
JS_AddInterruptCallback(cx, InterruptCallback);
|
||||||
|
|
||||||
if (!aPrimaryContext) {
|
if (!aPrimaryContext) {
|
||||||
|
|
|
@ -590,15 +590,21 @@ SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
SimulateActivityCallback(JSContext* cx, unsigned argc, Value* vp)
|
SimulateNoScriptActivity(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
// Sanity-check args.
|
// Sanity-check args.
|
||||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||||
if (args.length() != 1 || !args[0].isBoolean()) {
|
if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
|
||||||
JS_ReportErrorASCII(cx, "Wrong number of arguments");
|
JS_ReportErrorASCII(cx, "Expected a positive integer argument");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
xpc::SimulateActivityCallback(args[0].toBoolean());
|
|
||||||
|
// This mimics mozilla::SpinEventLoopUntil but instead of spinning the
|
||||||
|
// event loop we sleep, to make sure we don't run script.
|
||||||
|
xpc::AutoScriptActivity asa(false);
|
||||||
|
PR_Sleep(PR_SecondsToInterval(args[0].toInt32()));
|
||||||
|
|
||||||
|
args.rval().setUndefined();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +671,7 @@ static const JSFunctionSpec glob_functions[] = {
|
||||||
JS_FN("atob", xpc::Atob, 1,0),
|
JS_FN("atob", xpc::Atob, 1,0),
|
||||||
JS_FN("btoa", xpc::Btoa, 1,0),
|
JS_FN("btoa", xpc::Btoa, 1,0),
|
||||||
JS_FN("setInterruptCallback", SetInterruptCallback, 1,0),
|
JS_FN("setInterruptCallback", SetInterruptCallback, 1,0),
|
||||||
JS_FN("simulateActivityCallback", SimulateActivityCallback, 1,0),
|
JS_FN("simulateNoScriptActivity", SimulateNoScriptActivity, 1,0),
|
||||||
JS_FN("registerAppManifest", RegisterAppManifest, 1, 0),
|
JS_FN("registerAppManifest", RegisterAppManifest, 1, 0),
|
||||||
#ifdef ENABLE_TESTS
|
#ifdef ENABLE_TESTS
|
||||||
JS_FN("registerXPCTestComponents", RegisterXPCTestComponents, 0, 0),
|
JS_FN("registerXPCTestComponents", RegisterXPCTestComponents, 0, 0),
|
||||||
|
|
|
@ -373,7 +373,14 @@ public:
|
||||||
|
|
||||||
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
|
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
|
||||||
|
|
||||||
static void ActivityCallback(void* arg, bool active);
|
static bool RecordScriptActivity(bool aActive);
|
||||||
|
|
||||||
|
bool SetHasScriptActivity(bool aActive) {
|
||||||
|
bool oldValue = mHasScriptActivity;
|
||||||
|
mHasScriptActivity = aActive;
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
static bool InterruptCallback(JSContext* cx);
|
static bool InterruptCallback(JSContext* cx);
|
||||||
|
|
||||||
// Mapping of often used strings to jsid atoms that live 'forever'.
|
// Mapping of often used strings to jsid atoms that live 'forever'.
|
||||||
|
@ -461,6 +468,8 @@ private:
|
||||||
mozilla::TimeDuration mSlowScriptActualWait;
|
mozilla::TimeDuration mSlowScriptActualWait;
|
||||||
bool mTimeoutAccumulated;
|
bool mTimeoutAccumulated;
|
||||||
|
|
||||||
|
bool mHasScriptActivity;
|
||||||
|
|
||||||
// mPendingResult is used to implement Components.returnCode. Only really
|
// mPendingResult is used to implement Components.returnCode. Only really
|
||||||
// meaningful while calling through XPCWrappedJS.
|
// meaningful while calling through XPCWrappedJS.
|
||||||
nsresult mPendingResult;
|
nsresult mPendingResult;
|
||||||
|
|
|
@ -550,8 +550,14 @@ WindowGlobalOrNull(JSObject* aObj);
|
||||||
nsGlobalWindowInner*
|
nsGlobalWindowInner*
|
||||||
CurrentWindowOrNull(JSContext* cx);
|
CurrentWindowOrNull(JSContext* cx);
|
||||||
|
|
||||||
void
|
class MOZ_RAII AutoScriptActivity
|
||||||
SimulateActivityCallback(bool aActive);
|
{
|
||||||
|
bool mActive;
|
||||||
|
bool mOldValue;
|
||||||
|
public:
|
||||||
|
explicit AutoScriptActivity(bool aActive);
|
||||||
|
~AutoScriptActivity();
|
||||||
|
};
|
||||||
|
|
||||||
// This function may be used off-main-thread, in which case it is benignly
|
// This function may be used off-main-thread, in which case it is benignly
|
||||||
// racey.
|
// racey.
|
||||||
|
|
|
@ -23,13 +23,11 @@ async function testBody() {
|
||||||
// scheduling, we should never have more than 3 seconds of inactivity without
|
// scheduling, we should never have more than 3 seconds of inactivity without
|
||||||
// hibernating. To add some padding for automation, we mandate that hibernation
|
// hibernating. To add some padding for automation, we mandate that hibernation
|
||||||
// must begin between 2 and 5 seconds from now.
|
// must begin between 2 and 5 seconds from now.
|
||||||
await new Promise(resolve => {
|
|
||||||
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
timer.initWithCallback(resolve, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
simulateActivityCallback(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
simulateActivityCallback(true);
|
// Sleep for 10 seconds. Note: we don't use nsITimer here because then we may run
|
||||||
|
// arbitrary (idle) events involving script before it fires.
|
||||||
|
simulateNoScriptActivity(10);
|
||||||
|
|
||||||
busyWait(1000); // Give the watchdog time to wake up on the condvar.
|
busyWait(1000); // Give the watchdog time to wake up on the condvar.
|
||||||
var stateChange = Cu.getWatchdogTimestamp("ContextStateChange");
|
var stateChange = Cu.getWatchdogTimestamp("ContextStateChange");
|
||||||
startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart");
|
startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart");
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
#include "xpcpublic.h"
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/Likely.h"
|
#include "mozilla/Likely.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/Tuple.h"
|
#include "mozilla/Tuple.h"
|
||||||
|
@ -320,6 +322,14 @@ SpinEventLoopUntil(Pred&& aPredicate, nsIThread* aThread = nullptr)
|
||||||
{
|
{
|
||||||
nsIThread* thread = aThread ? aThread : NS_GetCurrentThread();
|
nsIThread* thread = aThread ? aThread : NS_GetCurrentThread();
|
||||||
|
|
||||||
|
// From a latency perspective, spinning the event loop is like leaving script
|
||||||
|
// and returning to the event loop. Tell the watchdog we stopped running
|
||||||
|
// script (until we return).
|
||||||
|
mozilla::Maybe<xpc::AutoScriptActivity> asa;
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
asa.emplace(false);
|
||||||
|
}
|
||||||
|
|
||||||
while (!aPredicate()) {
|
while (!aPredicate()) {
|
||||||
bool didSomething = NS_ProcessNextEvent(thread, true);
|
bool didSomething = NS_ProcessNextEvent(thread, true);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче