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:
Jan de Mooij 2018-09-04 16:59:56 +00:00
Родитель 56ca0b122a
Коммит 53f1370b5f
8 изменённых файлов: 100 добавлений и 40 удалений

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

@ -662,8 +662,11 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
{
MOZ_ASSERT(aGlobalObject);
if (aIsMainThread && gRunToCompletionListeners > 0) {
mDocShellEntryMonitor.emplace(cx(), aReason);
if (aIsMainThread) {
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)
: AutoJSAPI()
: mIsMainThread(NS_IsMainThread())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
Init();
if (mIsMainThread) {
mScriptActivity.emplace(true);
}
}
void
@ -828,8 +832,10 @@ AutoSlowOperation::CheckForInterrupt()
// For now we support only main thread!
if (mIsMainThread) {
// JS_CheckForInterrupt expects us to be in a realm.
JSAutoRealm ar(cx(), xpc::UnprivilegedJunkScope());
JS_CheckForInterrupt(cx());
AutoJSAPI jsapi;
if (jsapi.Init(xpc::UnprivilegedJunkScope())) {
JS_CheckForInterrupt(jsapi.cx());
}
}
}

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

@ -12,6 +12,7 @@
#include "MainThreadUtils.h"
#include "nsIGlobalObject.h"
#include "nsIPrincipal.h"
#include "xpcpublic.h"
#include "mozilla/Maybe.h"
@ -383,6 +384,7 @@ private:
friend nsIPrincipal* GetWebIDLCallerPrincipal();
Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
Maybe<xpc::AutoScriptActivity> mScriptActivity;
JS::AutoHideScriptedCaller mCallerOverride;
#ifdef MOZ_GECKO_PROFILER
AutoProfilerLabel mAutoProfilerLabel;
@ -455,17 +457,21 @@ private:
* 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
* through those callbacks.
* AutoSlowOperation puts a JSAutoRequest on the stack so that we don't continue
* to reset the watchdog and CheckForInterrupt can be then used to check whether
* JS execution should be interrupted.
* AutoSlowOperation puts an AutoScriptActivity on the stack so that we don't
* continue to reset the watchdog. CheckForInterrupt can then be used to check
* 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:
explicit AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
void CheckForInterrupt();
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
bool mIsMainThread;
Maybe<xpc::AutoScriptActivity> mScriptActivity;
};
/**

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

@ -551,28 +551,49 @@ XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
mWatchdogManager->GetTimestamp(aCategory, lock);
}
void
xpc::SimulateActivityCallback(bool aActive)
{
XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
}
// static
void
XPCJSContext::ActivityCallback(void* arg, bool active)
bool
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
// replaying, don't record/replay JS activity notifications.
if (recordreplay::IsRecordingOrReplaying()) {
return;
return oldValue;
}
if (!active) {
if (!aActive) {
ProcessHangMonitor::ClearHang();
}
xpccx->mWatchdogManager->RecordContextActivity(xpccx, aActive);
XPCJSContext* self = static_cast<XPCJSContext*>(arg);
self->mWatchdogManager->RecordContextActivity(self, active);
return oldValue;
}
AutoScriptActivity::AutoScriptActivity(bool aActive)
: mActive(aActive)
, mOldValue(XPCJSContext::RecordScriptActivity(aActive))
{
}
AutoScriptActivity::~AutoScriptActivity()
{
MOZ_ALWAYS_TRUE(mActive == XPCJSContext::RecordScriptActivity(mOldValue));
}
// static
@ -890,8 +911,6 @@ XPCJSContext::~XPCJSContext()
Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
#endif
js::SetActivityCallback(Context(), nullptr, nullptr);
// Clear any pending exception. It might be an XPCWrappedJS, and if we try
// to destroy it later we will crash.
SetPendingException(nullptr);
@ -926,6 +945,7 @@ XPCJSContext::XPCJSContext()
mWatchdogManager(GetWatchdogManager()),
mSlowScriptSecondHalf(false),
mTimeoutAccumulated(false),
mHasScriptActivity(false),
mPendingResult(NS_OK),
mActive(CONTEXT_INACTIVE),
mLastStateChange(PR_Now())
@ -1140,7 +1160,6 @@ XPCJSContext::Initialize(XPCJSContext* aPrimaryContext)
PROFILER_SET_JS_CONTEXT(cx);
js::SetActivityCallback(cx, ActivityCallback, this);
JS_AddInterruptCallback(cx, InterruptCallback);
if (!aPrimaryContext) {

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

@ -590,15 +590,21 @@ SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp)
}
static bool
SimulateActivityCallback(JSContext* cx, unsigned argc, Value* vp)
SimulateNoScriptActivity(JSContext* cx, unsigned argc, Value* vp)
{
// Sanity-check args.
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() != 1 || !args[0].isBoolean()) {
JS_ReportErrorASCII(cx, "Wrong number of arguments");
if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
JS_ReportErrorASCII(cx, "Expected a positive integer argument");
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;
}
@ -665,7 +671,7 @@ static const JSFunctionSpec glob_functions[] = {
JS_FN("atob", xpc::Atob, 1,0),
JS_FN("btoa", xpc::Btoa, 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),
#ifdef ENABLE_TESTS
JS_FN("registerXPCTestComponents", RegisterXPCTestComponents, 0, 0),

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

@ -373,7 +373,14 @@ public:
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);
// Mapping of often used strings to jsid atoms that live 'forever'.
@ -461,6 +468,8 @@ private:
mozilla::TimeDuration mSlowScriptActualWait;
bool mTimeoutAccumulated;
bool mHasScriptActivity;
// mPendingResult is used to implement Components.returnCode. Only really
// meaningful while calling through XPCWrappedJS.
nsresult mPendingResult;

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

@ -550,8 +550,14 @@ WindowGlobalOrNull(JSObject* aObj);
nsGlobalWindowInner*
CurrentWindowOrNull(JSContext* cx);
void
SimulateActivityCallback(bool aActive);
class MOZ_RAII AutoScriptActivity
{
bool mActive;
bool mOldValue;
public:
explicit AutoScriptActivity(bool aActive);
~AutoScriptActivity();
};
// This function may be used off-main-thread, in which case it is benignly
// racey.

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

@ -23,13 +23,11 @@ async function testBody() {
// scheduling, we should never have more than 3 seconds of inactivity without
// hibernating. To add some padding for automation, we mandate that hibernation
// 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.
var stateChange = Cu.getWatchdogTimestamp("ContextStateChange");
startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart");

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

@ -21,8 +21,10 @@
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "xpcpublic.h"
#include "mozilla/Atomics.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Tuple.h"
@ -320,6 +322,14 @@ SpinEventLoopUntil(Pred&& aPredicate, nsIThread* aThread = nullptr)
{
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()) {
bool didSomething = NS_ProcessNextEvent(thread, true);