diff options
author | Moonchild <moonchild@palemoon.org> | 2023-10-01 22:08:12 +0200 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2023-10-01 22:08:45 +0200 |
commit | 682e5a4727b8bdf33a670c5d08e9cb382f019a57 (patch) | |
tree | bc5528d6c6886f40cf9d771cf054da323ddec298 /dom | |
parent | efc3e99b90b248e3ea12316852dc9ac7e2a959e0 (diff) | |
parent | 16e405f81806ac85332a7c208d93e2edec7984d8 (diff) | |
download | uxp-682e5a4727b8bdf33a670c5d08e9cb382f019a57.tar.gz |
Merge branch '2323-timerclamp-workers'
This resolves #2323
Diffstat (limited to 'dom')
-rw-r--r-- | dom/workers/WorkerPrivate.cpp | 46 | ||||
-rw-r--r-- | dom/workers/WorkerPrivate.h | 19 |
2 files changed, 61 insertions, 4 deletions
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 2f2fa78d42..622a882a65 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -167,6 +167,9 @@ const nsIID kDEBUGWorkerEventTargetIID = { #endif +// The number of nested timeouts before we start clamping. HTML says 5. +const uint32_t kClampTimeoutNestingLevel = 5u; + template <class T> class AutoPtrComparator { @@ -1956,7 +1959,11 @@ NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequesto struct WorkerPrivate::TimeoutInfo { TimeoutInfo() - : mId(0), mIsInterval(false), mCanceled(false) + : mId(0) + , mNestingLevel(0) + , mIsInterval(false) + , mCanceled(false) + , mOnChromeWorker(false) { MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); } @@ -1976,12 +1983,33 @@ struct WorkerPrivate::TimeoutInfo return mTargetTime < aOther.mTargetTime; } + void AccumulateNestingLevel(const uint32_t& aBaseLevel) { + if (aBaseLevel < kClampTimeoutNestingLevel) { + mNestingLevel = aBaseLevel + 1; + return; + } + mNestingLevel = kClampTimeoutNestingLevel; + } + + void CalculateTargetTime() { + auto target = mInterval; + // Clamp timeout for workers, except chrome workers + if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) { + target = TimeDuration::Max( + mInterval, + TimeDuration::FromMilliseconds(Preferences::GetInt("dom.min_timeout_value"))); + } + mTargetTime = TimeStamp::Now() + target; + } + nsCOMPtr<nsIScriptTimeoutHandler> mHandler; mozilla::TimeStamp mTargetTime; mozilla::TimeDuration mInterval; int32_t mId; + uint32_t mNestingLevel; bool mIsInterval; bool mCanceled; + bool mOnChromeWorker; }; class WorkerJSContextStats final : public JS::RuntimeStats @@ -4130,6 +4158,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent, , mMainThreadEventTarget(do_GetMainThread()) , mErrorHandlerRecursionCount(0) , mNextTimeoutId(1) + , mCurrentTimerNestingLevel(0) , mStatus(Pending) , mFrozen(false) , mTimerRunning(false) @@ -6074,8 +6103,10 @@ WorkerPrivate::SetTimeout(JSContext* aCx, } nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo()); + newInfo->mOnChromeWorker = mIsChromeWorker; newInfo->mIsInterval = aIsInterval; newInfo->mId = timerId; + newInfo->AccumulateNestingLevel(this->mCurrentTimerNestingLevel); if (MOZ_UNLIKELY(timerId == INT32_MAX)) { NS_WARNING("Timeout ids overflowed!"); @@ -6188,14 +6219,21 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) // Guard against recursion. mRunningExpiredTimeouts = true; + MOZ_DIAGNOSTIC_ASSERT(data->mCurrentTimerNestingLevel == 0); + // Run expired timeouts. for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) { TimeoutInfo*& info = expiredTimeouts[index]; + AutoRestore<uint32_t> nestingLevel(this->mCurrentTimerNestingLevel); if (info->mCanceled) { continue; } + // Set current timer nesting level to current running timer handler's + // nesting level + this->mCurrentTimerNestingLevel = info->mNestingLevel; + LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n", this, info->mInterval.ToMilliseconds())); @@ -6273,8 +6311,10 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) info->mCanceled) { if (info->mIsInterval && !info->mCanceled) { // Reschedule intervals. - info->mTargetTime = info->mTargetTime + info->mInterval; - // Don't resort the list here, we'll do that at the end. + // Reschedule a timeout and, if needed, increase the nesting level. + info->AccumulateNestingLevel(info->mNestingLevel); + info->CalculateTargetTime(); + // Don't re-sort the list here, we'll do that at the end. ++index; } else { diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 351f5458f1..9effdccc9a 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -17,6 +17,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/AutoRestore.h" #include "mozilla/CondVar.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/DOMEventTargetHelper.h" @@ -212,6 +213,9 @@ protected: RefPtr<EventTarget> mEventTarget; nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables; + // True if the worker is used in the UI + bool mIsChromeWorker; + private: WorkerPrivate* mParent; nsString mScriptURL; @@ -241,7 +245,6 @@ private: uint32_t mParentWindowPausedDepth; Status mParentStatus; bool mParentFrozen; - bool mIsChromeWorker; bool mMainThreadObjectsForgotten; // mIsSecureContext is set once in our constructor; after that it can be read // from various threads. We could make this const if we were OK with setting @@ -1016,6 +1019,20 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate> uint32_t mErrorHandlerRecursionCount; uint32_t mNextTimeoutId; Status mStatus; + + // Tracks the current setTimeout/setInterval nesting level. + // When there isn't a TimeoutHandler on the stack, this will be 0. + // Whenever setTimeout/setInterval are called, a new TimeoutInfo will be + // created with a nesting level one more than the current nesting level, + // saturating at the kClampTimeoutNestingLevel. + // + // When RunExpiredTimeouts is run, it sets this value to the + // TimeoutInfo::mNestingLevel for the duration of + // the WorkerScriptTimeoutHandler::Call which will explicitly trigger a + // microtask checkpoint so that any immediately-resolved promises will + // still see the nesting level. + uint32_t mCurrentTimerNestingLevel; + bool mFrozen; bool mTimerRunning; bool mRunningExpiredTimeouts; |