Files
mayankleoboy1 869fc80031 Bug 1997529 - Make CancelIdleCallback() O(1) by indexing idle requests by handle. r=smaug
cancelIdleCallback() walked mIdleRequestCallbacks looking for a matching
handle, so a page with many outstanding requests is O(N^2) in the number of
cancels. In the testcase from Bug 1997529 comment 0 (120k requests, ~60k
cancels) that loop is 96% of a 15.5s microtask checkpoint.

Index the requests by handle alongside the list, following Timeout and
TimeoutManager::Timeouts. The map is maintained by
IdleRequest::RemoveFromList() rather than by each call site, because
IdleRequest::Unlink() takes itself out of the list directly without going
through RemoveIdleCallback(); a map updated only there would keep a dangling
entry. Inheriting from LinkedListElement protected, as Timeout does, makes the
base remove() and removeFrom() inaccessible to callers, so every removal has to
go through RemoveFromList().

Differential Revision: https://phabricator.services.mozilla.com/D323469
2026-09-06 12:44:25 +00:00

74 lines
2.6 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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/. */
#include "IdleRequest.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/IdleDeadline.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/dom/WebTaskScheduler.h"
#include "mozilla/dom/WindowBinding.h"
#include "nsComponentManagerUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsPIDOMWindow.h"
namespace mozilla::dom {
IdleRequest::IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle)
: mCallback(aCallback), mHandle(aHandle), mTimeoutHandle(Nothing()) {
MOZ_DIAGNOSTIC_ASSERT(mCallback);
}
IdleRequest::~IdleRequest() { SetContainer(nullptr); }
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
if (tmp->isInList()) {
tmp->RemoveFromList();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
void IdleRequest::SetTimeoutHandle(int32_t aHandle) {
mTimeoutHandle = Some(aHandle);
}
int32_t IdleRequest::GetTimeoutHandle() const {
MOZ_DIAGNOSTIC_ASSERT(mTimeoutHandle.isSome());
return mTimeoutHandle.value();
}
void IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow,
DOMHighResTimeStamp aDeadline, bool aDidTimeout) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(mCallback);
RefPtr<IdleDeadline> deadline =
new IdleDeadline(aWindow, aDidTimeout, aDeadline);
RefPtr<IdleRequestCallback> callback(std::move(mCallback));
MOZ_ASSERT(!mCallback);
RefPtr<nsGlobalWindowInner> innerWindow = nsGlobalWindowInner::Cast(aWindow);
// https://wicg.github.io/scheduling-apis/#sec-patches-invoke-idle-callbacks
// Let state be a new scheduling state.
RefPtr<WebTaskSchedulingState> newState = new WebTaskSchedulingState();
// Set states priority source to the result of creating a fixed priority
// unabortable task signal given "background" and realm.
newState->SetPrioritySource(
TaskSignal::Create(aWindow->AsGlobal(), TaskPriority::Background));
// Set event loops current scheduling state to state.
innerWindow->SetWebTaskSchedulingState(newState);
callback->Call(*deadline, "requestIdleCallback handler");
// Set event loops current scheduling state to null.
innerWindow->SetWebTaskSchedulingState(nullptr);
}
} // namespace mozilla::dom