author | Wes Kocher <wkocher@mozilla.com> |
Thu, 31 Aug 2017 17:08:29 -0700 | |
changeset 378144 | d4cea477b7e43db13c56656b6c24f4391538544c |
parent 378143 | d7cae06749f131a1fdffed1926fe526f7cfc89ee |
child 378145 | ded0b612dba768607ab11687d874b974a9855201 |
push id | 94412 |
push user | archaeopteryx@coole-files.de |
push date | Fri, 01 Sep 2017 08:46:09 +0000 |
treeherder | mozilla-inbound@d56571d7f1be [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | backout |
bugs | 1376507 |
milestone | 57.0a1 |
backs out | 266611b269cc876858346826d178a1e8fef0e0d4 04ecce0d1392376ddc40f1dd6655bbbc90bc6a8a |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/xpconnect/src/XPCJSContext.cpp | file | annotate | diff | comparison | revisions | |
js/xpconnect/src/xpcprivate.h | file | annotate | diff | comparison | revisions |
--- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -74,23 +74,20 @@ using namespace mozilla; using namespace xpc; using namespace JS; using mozilla::dom::PerThreadAtomCache; using mozilla::dom::AutoEntryScript; static void WatchdogMain(void* arg); class Watchdog; class WatchdogManager; -class MOZ_RAII AutoLockWatchdog final -{ - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +class AutoLockWatchdog { Watchdog* const mWatchdog; - public: - explicit AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + explicit AutoLockWatchdog(Watchdog* aWatchdog); ~AutoLockWatchdog(); }; class Watchdog { public: explicit Watchdog(WatchdogManager* aManager) : mManager(aManager) @@ -230,20 +227,25 @@ class Watchdog #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time" #define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT "dom.max_ext_content_script_run_time" class WatchdogManager : public nsIObserver { public: NS_DECL_ISUPPORTS - explicit WatchdogManager() + explicit WatchdogManager(XPCJSContext* aContext) : mContext(aContext) + , mContextState(CONTEXT_INACTIVE) { - // All the timestamps start at zero. + // All the timestamps start at zero except for context state change. PodArrayZero(mTimestamps); + mTimestamps[TimestampContextStateChange] = PR_Now(); + + // Enable the watchdog, if appropriate. + RefreshWatchdog(); // Register ourselves as an observer to get updates on the pref. mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog"); mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT); mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME); mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT); } @@ -269,107 +271,63 @@ class WatchdogManager : public nsIObserv NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { RefreshWatchdog(); return NS_OK; } - void - RegisterContext(XPCJSContext* aContext) - { - MOZ_ASSERT(NS_IsMainThread()); - AutoLockWatchdog lock(mWatchdog); - - if (aContext->mActive == XPCJSContext::CONTEXT_ACTIVE) { - mActiveContexts.insertBack(aContext); - } else { - mInactiveContexts.insertBack(aContext); - } - - // Enable the watchdog, if appropriate. - RefreshWatchdog(); - } - - void - UnregisterContext(XPCJSContext* aContext) - { - MOZ_ASSERT(NS_IsMainThread()); - AutoLockWatchdog lock(mWatchdog); - - // aContext must be in one of our two lists, simply remove it. - aContext->LinkedListElement<XPCJSContext>::remove(); - } - // Context statistics. These live on the watchdog manager, are written // from the main thread, and are read from the watchdog thread (holding // the lock in each case). - void RecordContextActivity(XPCJSContext* aContext, bool active) + void + RecordContextActivity(bool active) { // The watchdog reads this state, so acquire the lock before writing it. MOZ_ASSERT(NS_IsMainThread()); - AutoLockWatchdog lock(mWatchdog); + Maybe<AutoLockWatchdog> lock; + if (mWatchdog) + lock.emplace(mWatchdog); // Write state. - aContext->mLastStateChange = PR_Now(); - aContext->mActive = active ? XPCJSContext::CONTEXT_ACTIVE : - XPCJSContext::CONTEXT_INACTIVE; - UpdateContextLists(aContext); + mTimestamps[TimestampContextStateChange] = PR_Now(); + mContextState = active ? CONTEXT_ACTIVE : CONTEXT_INACTIVE; // The watchdog may be hibernating, waiting for the context to go // active. Wake it up if necessary. if (active && mWatchdog && mWatchdog->Hibernating()) mWatchdog->WakeUp(); } - - bool IsAnyContextActive() - { - return !mActiveContexts.isEmpty(); - } - PRTime TimeSinceLastActiveContext() + bool IsContextActive() { return mContextState == CONTEXT_ACTIVE; } + PRTime TimeSinceLastContextStateChange() { - // Must be called on the watchdog thread with the lock held. - MOZ_ASSERT(!NS_IsMainThread()); - if (mWatchdog) - PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock()); - MOZ_ASSERT(mActiveContexts.isEmpty()); - MOZ_ASSERT(!mInactiveContexts.isEmpty()); - - // We store inactive contexts with the most recently added inactive - // context at the end of the list. - return PR_Now() - mInactiveContexts.getLast()->mLastStateChange; + return PR_Now() - GetTimestamp(TimestampContextStateChange); } + // Note - Because of the context activity timestamp, these are read and + // written from both threads. void RecordTimestamp(WatchdogTimestampCategory aCategory) { - // Must be called on the watchdog thread with the lock held. - MOZ_ASSERT(!NS_IsMainThread()); - if (mWatchdog) - PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock()); - MOZ_ASSERT(aCategory != TimestampContextStateChange, - "Use RecordContextActivity to update this"); - + // The watchdog thread always holds the lock when it runs. + Maybe<AutoLockWatchdog> maybeLock; + if (NS_IsMainThread() && mWatchdog) + maybeLock.emplace(mWatchdog); mTimestamps[aCategory] = PR_Now(); } - - PRTime GetContextTimestamp(XPCJSContext* aContext, - const AutoLockWatchdog& aProofOfLock) + PRTime GetTimestamp(WatchdogTimestampCategory aCategory) { - return aContext->mLastStateChange; - } - - PRTime GetTimestamp(WatchdogTimestampCategory aCategory, - const AutoLockWatchdog& aProofOfLock) - { - MOZ_ASSERT(aCategory != TimestampContextStateChange, - "Use GetContextTimestamp to retrieve this"); + // The watchdog thread always holds the lock when it runs. + Maybe<AutoLockWatchdog> maybeLock; + if (NS_IsMainThread() && mWatchdog) + maybeLock.emplace(mWatchdog); return mTimestamps[aCategory]; } + XPCJSContext* Context() { return mContext; } Watchdog* GetWatchdog() { return mWatchdog; } void RefreshWatchdog() { bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true); if (wantWatchdog != !!mWatchdog) { if (wantWatchdog) StartWatchdog(); @@ -400,72 +358,34 @@ class WatchdogManager : public nsIObserv void StopWatchdog() { MOZ_ASSERT(mWatchdog); mWatchdog->Shutdown(); mWatchdog = nullptr; } - template<class Callback> - void ForAllActiveContexts(Callback&& aCallback) - { - // This function must be called on the watchdog thread with the lock held. - MOZ_ASSERT(!NS_IsMainThread()); - if (mWatchdog) - PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock()); - - for (auto* context = mActiveContexts.getFirst(); context; - context = context->LinkedListElement<XPCJSContext>::getNext()) { - if (!aCallback(context)) { - return; - } - } - } - private: - void UpdateContextLists(XPCJSContext* aContext) - { - // Given aContext whose activity state or timestamp has just changed, - // put it back in the proper position in the proper list. - aContext->LinkedListElement<XPCJSContext>::remove(); - auto& list = aContext->mActive == XPCJSContext::CONTEXT_ACTIVE ? - mActiveContexts : mInactiveContexts; - - // Either the new list is empty or aContext must be more recent than - // the existing last element. - MOZ_ASSERT_IF(!list.isEmpty(), - list.getLast()->mLastStateChange < aContext->mLastStateChange); - list.insertBack(aContext); - } - - LinkedList<XPCJSContext> mActiveContexts; - LinkedList<XPCJSContext> mInactiveContexts; + XPCJSContext* mContext; nsAutoPtr<Watchdog> mWatchdog; - // We store ContextStateChange on the contexts themselves. - PRTime mTimestamps[kWatchdogTimestampCategoryCount - 1]; + enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mContextState; + PRTime mTimestamps[TimestampCount]; }; NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver) -AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : mWatchdog(aWatchdog) +AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog) : mWatchdog(aWatchdog) { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (mWatchdog) { - PR_Lock(mWatchdog->GetLock()); - } + PR_Lock(mWatchdog->GetLock()); } AutoLockWatchdog::~AutoLockWatchdog() { - if (mWatchdog) { - PR_Unlock(mWatchdog->GetLock()); - } + PR_Unlock(mWatchdog->GetLock()); } static void WatchdogMain(void* arg) { mozilla::AutoProfilerRegisterThread registerThread("JS Watchdog"); NS_SetCurrentThreadName("JS Watchdog"); @@ -474,18 +394,18 @@ WatchdogMain(void* arg) // Lock lasts until we return AutoLockWatchdog lock(self); MOZ_ASSERT(self->Initialized()); MOZ_ASSERT(!self->ShuttingDown()); while (!self->ShuttingDown()) { // Sleep only 1 second if recently (or currently) active; otherwise, hibernate - if (manager->IsAnyContextActive() || - manager->TimeSinceLastActiveContext() <= PRTime(2*PR_USEC_PER_SEC)) + if (manager->IsContextActive() || + manager->TimeSinceLastContextStateChange() <= PRTime(2*PR_USEC_PER_SEC)) { self->Sleep(PR_TicksPerSecond()); } else { manager->RecordTimestamp(TimestampWatchdogHibernateStart); self->Hibernate(); manager->RecordTimestamp(TimestampWatchdogHibernateStop); } @@ -501,49 +421,37 @@ WatchdogMain(void* arg) // invoke the interrupt callback after only half the timeout has // elapsed. The callback simply records the fact that it was called in // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2) // seconds and invoke the callback again. This time around it sees // mSlowScriptSecondHalf is set and so it shows the slow script // dialog. If the computer is put to sleep during one of the (timeout/2) // periods, the script still has the other (timeout/2) seconds to // finish. - if (manager->IsAnyContextActive()) { + PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2; + if (manager->IsContextActive() && + manager->TimeSinceLastContextStateChange() >= usecs) + { bool debuggerAttached = false; nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1"); if (dbg) dbg->GetIsDebuggerAttached(&debuggerAttached); - if (debuggerAttached) { - // We won't be interrupting these scripts anyway. - continue; - } - - PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2; - manager->ForAllActiveContexts([usecs, manager, &lock](XPCJSContext* aContext) -> bool { - auto timediff = PR_Now() - manager->GetContextTimestamp(aContext, lock); - if (timediff > usecs) { - JS_RequestInterruptCallback(aContext->Context()); - return true; - } - return false; - }); + if (!debuggerAttached) + JS_RequestInterruptCallback(manager->Context()->Context()); } } // Tell the manager that we've shut down. self->Finished(); } PRTime XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory) { - AutoLockWatchdog lock(mWatchdogManager->GetWatchdog()); - return aCategory == TimestampContextStateChange ? - mWatchdogManager->GetContextTimestamp(this, lock) : - mWatchdogManager->GetTimestamp(aCategory, lock); + return mWatchdogManager->GetTimestamp(aCategory); } void xpc::SimulateActivityCallback(bool aActive) { XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive); } @@ -551,17 +459,17 @@ xpc::SimulateActivityCallback(bool aActi void XPCJSContext::ActivityCallback(void* arg, bool active) { if (!active) { ProcessHangMonitor::ClearHang(); } XPCJSContext* self = static_cast<XPCJSContext*>(arg); - self->mWatchdogManager->RecordContextActivity(self, active); + self->mWatchdogManager->RecordContextActivity(active); } static inline bool IsWebExtensionPrincipal(nsIPrincipal* principal, nsAString& addonId) { return (NS_SUCCEEDED(principal->GetAddonId(addonId)) && !addonId.IsEmpty()); } @@ -875,24 +783,19 @@ XPCJSContext::~XPCJSContext() 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); xpc_DelocalizeContext(Context()); - // If we're the last XPCJSContext around, clean up the watchdog manager. - mWatchdogManager->UnregisterContext(this); - if (--sInstanceCount == 0) { - if (mWatchdogManager->GetWatchdog()) - mWatchdogManager->StopWatchdog(); - mWatchdogManager->Shutdown(); - sWatchdogInstance = nullptr; - } + if (mWatchdogManager->GetWatchdog()) + mWatchdogManager->StopWatchdog(); + mWatchdogManager->Shutdown(); if (mCallContext) mCallContext->SystemIsBeingShutDown(); auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(Context())); delete rtPrivate; JS_SetContextPrivate(Context(), nullptr); @@ -901,28 +804,23 @@ XPCJSContext::~XPCJSContext() gTlsContext.set(nullptr); } XPCJSContext::XPCJSContext() : mCallContext(nullptr), mAutoRoots(nullptr), mResolveName(JSID_VOID), mResolvingWrapper(nullptr), - mWatchdogManager(GetWatchdogManager()), + mWatchdogManager(new WatchdogManager(this)), mSlowScriptSecondHalf(false), mTimeoutAccumulated(false), - mPendingResult(NS_OK), - mActive(CONTEXT_INACTIVE), - mLastStateChange(PR_Now()) + mPendingResult(NS_OK) { MOZ_COUNT_CTOR_INHERITED(XPCJSContext, CycleCollectedJSContext); MOZ_RELEASE_ASSERT(!gTlsContext.get()); - MOZ_ASSERT(mWatchdogManager); - ++sInstanceCount; - mWatchdogManager->RegisterContext(this); gTlsContext.set(this); } /* static */ XPCJSContext* XPCJSContext::Get() { return gTlsContext.get(); } @@ -1103,37 +1001,16 @@ XPCJSContext::Initialize(XPCJSContext* a #ifdef FUZZING Preferences::RegisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this); #endif return NS_OK; } // static -uint32_t -XPCJSContext::sInstanceCount; - -// static -StaticRefPtr<WatchdogManager> -XPCJSContext::sWatchdogInstance; - -// static -WatchdogManager* -XPCJSContext::GetWatchdogManager() -{ - if (sWatchdogInstance) { - return sWatchdogInstance; - } - - MOZ_ASSERT(sInstanceCount == 0); - sWatchdogInstance = new WatchdogManager(); - return sWatchdogInstance; -} - -// static void XPCJSContext::InitTLS() { MOZ_RELEASE_ASSERT(gTlsContext.init()); } // static XPCJSContext*
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -71,17 +71,16 @@ #include "mozilla/Alignment.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/DebugOnly.h" -#include "mozilla/DefineEnum.h" #include "mozilla/GuardObjects.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "mozilla/dom/ScriptSettings.h" @@ -334,22 +333,24 @@ private: /***************************************************************************/ // In the current xpconnect system there can only be one XPCJSContext. // So, xpconnect can only be used on one JSContext within the process. class WatchdogManager; -MOZ_DEFINE_ENUM(WatchdogTimestampCategory, ( +enum WatchdogTimestampCategory +{ + TimestampContextStateChange = 0, TimestampWatchdogWakeup, TimestampWatchdogHibernateStart, TimestampWatchdogHibernateStop, - TimestampContextStateChange -)); + TimestampCount +}; class AsyncFreeSnowWhite; template <class StringType> class ShortLivedStringBuffer { public: StringType* Create() @@ -390,17 +391,16 @@ public: #endif } private: mozilla::Maybe<StringType> mStrings[2]; }; class XPCJSContext final : public mozilla::CycleCollectedJSContext - , public mozilla::LinkedListElement<XPCJSContext> { public: static void InitTLS(); static XPCJSContext* NewXPCJSContext(XPCJSContext* aPrimaryContext); static XPCJSContext* Get(); XPCJSRuntime* Runtime() const; @@ -487,22 +487,17 @@ private: MOZ_IS_CLASS_INIT nsresult Initialize(XPCJSContext* aPrimaryContext); XPCCallContext* mCallContext; AutoMarkingPtr* mAutoRoots; jsid mResolveName; XPCWrappedNative* mResolvingWrapper; - WatchdogManager* mWatchdogManager; - - // Number of XPCJSContexts currently alive. - static uint32_t sInstanceCount; - static mozilla::StaticRefPtr<WatchdogManager> sWatchdogInstance; - static WatchdogManager* GetWatchdogManager(); + RefPtr<WatchdogManager> mWatchdogManager; // If we spend too much time running JS code in an event handler, then we // want to show the slow script UI. The timeout T is controlled by prefs. We // invoke the interrupt callback once after T/2 seconds and set // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the // interrupt callback again. Since mSlowScriptSecondHalf is now true, it // shows the slow script UI. The reason we invoke the callback twice is to // ensure that putting the computer to sleep while running a script doesn't @@ -520,23 +515,18 @@ private: // Accumulates total time we actually waited for telemetry mozilla::TimeDuration mSlowScriptActualWait; bool mTimeoutAccumulated; // mPendingResult is used to implement Components.returnCode. Only really // meaningful while calling through XPCWrappedJS. nsresult mPendingResult; - // These members must be accessed via WatchdogManager. - enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mActive; - PRTime mLastStateChange; - friend class XPCJSRuntime; friend class Watchdog; - friend class WatchdogManager; friend class AutoLockWatchdog; }; class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime { public: static XPCJSRuntime* Get();