commit 52db7a59d68f8fb25d63941c7a5ad1d1ab28903b
parent 8e5f0ed2176163fcb4dc325f8033e8a2de12ba5e
Author: acreskeyMoz <acreskey@mozilla.com>
Date: Tue, 14 Oct 2025 20:46:09 +0000
Bug 1635092 - Save one main thread loop to nsHttpChannel::AsyncOpen of IPC'ed channels r=necko-reviewers,kershaw,valentin
Adds a new serial event target that executes the runnable immediately if already on the main thread. This allows then ThenValue to execute immediately instead of being dispatched to the back of the main thread event target, when possible.
Used here to prevent WaitForBgParent promise resolution round tripping to the main thread for UrgentStart channels.
Differential Revision: https://phabricator.services.mozilla.com/D258668
Diffstat:
6 files changed, 143 insertions(+), 4 deletions(-)
diff --git a/netwerk/base/ExecuteIfOnMainThreadEventTarget.cpp b/netwerk/base/ExecuteIfOnMainThreadEventTarget.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "ExecuteIfOnMainThreadEventTarget.h"
+#include "mozilla/ClearOnShutdown.h"
+
+namespace mozilla::net {
+
+NS_IMPL_ISUPPORTS(ExecuteIfOnMainThreadEventTarget, nsIEventTarget,
+ nsISerialEventTarget)
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::Dispatch(
+ already_AddRefed<nsIRunnable> aRunnable, DispatchFlags aFlags) {
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIRunnable> runnable(aRunnable);
+ return runnable->Run();
+ }
+ return GetMainThreadSerialEventTarget()->Dispatch(std::move(aRunnable),
+ aFlags);
+}
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable,
+ DispatchFlags aFlags) {
+ return Dispatch(do_AddRef(aRunnable), aFlags);
+}
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
+ uint32_t) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::RegisterShutdownTask(nsITargetShutdownTask*) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::UnregisterShutdownTask(
+ nsITargetShutdownTask*) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ExecuteIfOnMainThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
+ *aIsOnCurrentThread = NS_IsMainThread();
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(bool)
+ExecuteIfOnMainThreadEventTarget::IsOnCurrentThreadInfallible() {
+ return NS_IsMainThread();
+}
+
+nsISerialEventTarget* ExecuteIfOnMainThreadEventTarget::Get() {
+ static nsCOMPtr<nsISerialEventTarget> sTarget;
+ if (!sTarget) {
+ sTarget = new ExecuteIfOnMainThreadEventTarget();
+ ClearOnShutdown(&sTarget);
+ }
+
+ return sTarget.get();
+}
+
+} // namespace mozilla::net
diff --git a/netwerk/base/ExecuteIfOnMainThreadEventTarget.h b/netwerk/base/ExecuteIfOnMainThreadEventTarget.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef ExecuteIfOnMainThreadEventTarget_h__
+#define ExecuteIfOnMainThreadEventTarget_h__
+
+#include "nsISerialEventTarget.h"
+
+namespace mozilla {
+namespace net {
+
+/*
+ An event target that will execute the runnable immediately if on the main
+ thread, avoiding a dispatch to the end of queue. Otherwise the runnable will
+ be dispatched to the main thread.
+ */
+class ExecuteIfOnMainThreadEventTarget final : public nsISerialEventTarget {
+ public:
+ ExecuteIfOnMainThreadEventTarget() {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIEVENTTARGET
+ NS_DECL_NSISERIALEVENTTARGET
+
+ static nsISerialEventTarget* Get();
+
+ private:
+ ~ExecuteIfOnMainThreadEventTarget() = default;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif
diff --git a/netwerk/base/Tickler.cpp b/netwerk/base/Tickler.cpp
@@ -12,6 +12,10 @@
# include "nsServiceManagerUtils.h"
# include "nsThreadUtils.h"
# include "prnetdb.h"
+# include "nsXULAppAPI.h"
+# include "nsIPrefService.h"
+# include "nsIPrefBranch.h"
+
# include "mozilla/java/GeckoAppShellWrappers.h"
# include "mozilla/jni/Utils.h"
# include "nsXULAppAPI.h"
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
@@ -164,6 +164,7 @@ EXPORTS.mozilla.net += [
"Dashboard.h",
"DashboardTypes.h",
"DefaultURI.h",
+ "ExecuteIfOnMainThreadEventTarget.h",
"InterceptionInfo.h",
"IPv4Parser.h",
"NetworkConnectivityService.h",
@@ -186,6 +187,7 @@ UNIFIED_SOURCES += [
"Dashboard.cpp",
"DefaultURI.cpp",
"EventTokenBucket.cpp",
+ "ExecuteIfOnMainThreadEventTarget.cpp",
"InterceptionInfo.cpp",
"IPv4Parser.cpp",
"LNAPermissionRequest.cpp",
diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -20,7 +20,9 @@
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/ExecuteIfOnMainThreadEventTarget.h"
#include "mozilla/net/CookieServiceParent.h"
+#include "nsIClassOfService.h"
#include "mozilla/Components.h"
#include "mozilla/InputStreamLengthHelper.h"
#include "mozilla/IntegerPrintfMacros.h"
@@ -654,9 +656,10 @@ bool HttpChannelParent::DoAsyncOpen(
// Wait for HttpBackgroundChannel to continue the async open procedure.
++mAsyncOpenBarrier;
RefPtr<HttpChannelParent> self = this;
+ nsCOMPtr<nsISerialEventTarget> eventTarget = GetEventTargetForBgParentWait();
WaitForBgParent(mChannel->ChannelId())
->Then(
- GetMainThreadSerialEventTarget(), __func__,
+ eventTarget, __func__,
[self]() {
self->mRequest.Complete();
self->TryInvokeAsyncOpen(NS_OK);
@@ -737,10 +740,10 @@ bool HttpChannelParent::ConnectChannel(const uint32_t& registrarId) {
MOZ_ASSERT(mPromise.IsEmpty());
// Waiting for background channel
RefPtr<HttpChannelParent> self = this;
+ nsCOMPtr<nsISerialEventTarget> eventTarget = GetEventTargetForBgParentWait();
WaitForBgParent(mChannel->ChannelId())
->Then(
- GetMainThreadSerialEventTarget(), __func__,
- [self]() { self->mRequest.Complete(); },
+ eventTarget, __func__, [self]() { self->mRequest.Complete(); },
[self](const nsresult& aResult) {
NS_ERROR("failed to establish the background channel");
self->mRequest.Complete();
@@ -983,9 +986,11 @@ HttpChannelParent::ContinueVerification(
// Otherwise, wait for the background channel.
nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
if (mChannel) {
+ nsCOMPtr<nsISerialEventTarget> eventTarget =
+ GetEventTargetForBgParentWait();
WaitForBgParent(mChannel->ChannelId())
->Then(
- GetMainThreadSerialEventTarget(), __func__,
+ eventTarget, __func__,
[callback]() { callback->ReadyToVerify(NS_OK); },
[callback](const nsresult& aResult) {
NS_ERROR("failed to establish the background channel");
@@ -1552,6 +1557,21 @@ HttpChannelParent::OnDataAvailable(nsIRequest* aRequest,
return NS_OK;
}
+// Get the appropriate event target for background parent operations based on
+// the channel's class of service flags: Urgent channels use
+// ExecuteIfOnMainThreadEventTarget to avoid the async main-thread dispatch
+// overhead when already on the main thread. Non-urgent channels use the regular
+// event queue to prevent head-of-line blocking that would delay other main
+// thread events.
+nsCOMPtr<nsISerialEventTarget>
+HttpChannelParent::GetEventTargetForBgParentWait() {
+ uint32_t classOfServiceFlags = 0;
+ mChannel->GetClassFlags(&classOfServiceFlags);
+ return (classOfServiceFlags & nsIClassOfService::UrgentStart)
+ ? ExecuteIfOnMainThreadEventTarget::Get()
+ : GetMainThreadSerialEventTarget();
+}
+
bool HttpChannelParent::NeedFlowControl() {
if (mCacheNeedFlowControlInitialized) {
return mNeedFlowControl;
diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
@@ -243,6 +243,12 @@ class HttpChannelParent final : public nsIInterfaceRequestor,
// consumed quickly enough. Otherwise, memory explosion could happen.
bool NeedFlowControl();
+ // Get the appropriate event target for background parent operations based on
+ // channel's class of service flags: synchronous event target for urgent
+ // channels, queued for others to balance responsiveness and prevent
+ // head-of-line blocking.
+ nsCOMPtr<nsISerialEventTarget> GetEventTargetForBgParentWait();
+
bool IsRedirectDueToAuthRetry(uint32_t redirectFlags);
int32_t mSendWindowSize;