Bug 1914632 - delete the actors during shutdown for worker keepalive requests.r=edenchuang

Differential Revision: https://phabricator.services.mozilla.com/D222437
This commit is contained in:
smayya
2024-09-24 11:30:22 +00:00
parent 250a9f6bba
commit fe6c28d58c
5 changed files with 137 additions and 46 deletions

View File

@@ -80,8 +80,8 @@ FetchParent::FetchParent() : mID(nsID::GenerateUUID()) {
FetchParent::~FetchParent() {
FETCH_LOG(("FetchParent::~FetchParent [%p]", this));
// MOZ_ASSERT(!mBackgroundEventTarget);
MOZ_ASSERT(!mResponsePromises);
MOZ_ASSERT(mActorDestroyed && mIsDone);
mResponsePromises = nullptr;
}
IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
@@ -178,7 +178,9 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
self->mCookieJarSettings, self->mNeedOnDataAvailable,
self->mCSPEventListener, self->mAssociatedBrowsingContextID,
self->mBackgroundEventTarget, self->mID,
self->mIsThirdPartyContext})));
self->mIsThirdPartyContext,
MozPromiseRequestHolder<FetchServiceResponseEndPromise>(),
self->mPromise})));
} else {
MOZ_ASSERT(self->mRequest->GetKeepalive());
self->mResponsePromises =
@@ -189,6 +191,29 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
self->mBackgroundEventTarget, self->mID})));
}
bool isResolved =
self->mResponsePromises->GetResponseEndPromise()->IsResolved();
if (!isResolved && self->mIsWorkerFetch) {
// track only unresolved promises for worker fetch requests
// this is needed for clean-up of keepalive requests
self->mResponsePromises->GetResponseEndPromise()
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self](ResponseEndArgs&& aArgs) mutable {
AssertIsOnMainThread();
MOZ_ASSERT(self->mPromise);
self->mPromise->Resolve(true, __func__);
self->mResponsePromises = nullptr;
},
[self](CopyableErrorResult&& aErr) mutable {
AssertIsOnMainThread();
MOZ_ASSERT(self->mPromise);
self->mPromise->Reject(aErr.StealNSResult(), __func__);
self->mResponsePromises = nullptr;
})
->Track(fetchService->GetResponseEndPromiseHolder(
self->mResponsePromises));
} else {
self->mResponsePromises->GetResponseEndPromise()->Then(
GetMainThreadSerialEventTarget(), __func__,
[self](ResponseEndArgs&& aArgs) mutable {
@@ -203,6 +228,7 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
self->mPromise->Reject(aErr.StealNSResult(), __func__);
self->mResponsePromises = nullptr;
});
}
});
MOZ_ALWAYS_SUCCEEDS(
@@ -220,25 +246,28 @@ IPCResult FetchParent::RecvAbortFetchOp(bool aForceAbort) {
return IPC_OK();
}
if (!aForceAbort && mRequest && mRequest->GetKeepalive() && !mIsWorkerFetch) {
if (!aForceAbort && mRequest && mRequest->GetKeepalive()) {
// Keeping FetchParent/FetchChild alive for the main-thread keepalive fetch
// here is a temporary solution. The cancel logic should always be handled
// in FetchInstance::Cancel() once all main-thread fetch routing through
// PFetch.
if (!mIsWorkerFetch) {
FETCH_LOG(("Skip aborting fetch as the request is marked keepalive"));
return IPC_OK();
}
} else {
mIsDone = true;
}
RefPtr<FetchParent> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [self, forceAbort = aForceAbort]() mutable {
FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable"));
AssertIsOnMainThread();
if (self->mResponsePromises) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
fetchService->CancelFetch(std::move(self->mResponsePromises));
fetchService->CancelFetch(std::move(self->mResponsePromises),
forceAbort);
}
});
@@ -263,8 +292,8 @@ void FetchParent::OnResponseAvailableInternal(
return;
}
// To monitor the stream status between processes, response's body can not be
// serialized as RemoteLazyInputStream. Such that stream close can be
// To monitor the stream status between processes, response's body can not
// be serialized as RemoteLazyInputStream. Such that stream close can be
// propagated to FetchDriver in the parent process.
aResponse->SetSerializeAsLazy(false);

View File

@@ -11,6 +11,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PFetchParent.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/dom/FetchService.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsCOMPtr.h"

View File

@@ -309,7 +309,7 @@ bool FetchService::FetchInstance::IsLocalHostFetch() const {
return res;
}
void FetchService::FetchInstance::Cancel() {
void FetchService::FetchInstance::Cancel(bool aForceAbort) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
@@ -320,6 +320,38 @@ void FetchService::FetchInstance::Cancel() {
// FetchInstance::OnResponseEnd() to resolve the pending promises.
// Otherwise, resolving the pending promises here.
if (mFetchDriver) {
// if keepalive is active and it is NOT user initiated Abort, then
// do not cancel the request.
if (mRequest->GetKeepalive() && !aForceAbort) {
FETCH_LOG(("Cleaning up the worker for keepalive[%p]", this));
MOZ_ASSERT(mArgs.is<WorkerFetchArgs>());
if (mArgs.is<WorkerFetchArgs>()) {
// delete the actors for cleanup for worker keep-alive requests.
// Non-worker keepalive requests need actors to be active until request
// completion, because we update request quota per load-group in
// FetchChild::ActorDestroy.
MOZ_ASSERT((mArgs.as<WorkerFetchArgs>().mFetchParentPromise));
if (mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Exists()) {
FETCH_LOG(
("FetchInstance::Cancel() [%p] mResponseEndPromiseHolder exists",
this));
mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Disconnect();
MOZ_ASSERT(
!mArgs.as<WorkerFetchArgs>().mFetchParentPromise->IsResolved());
if (!mArgs.as<WorkerFetchArgs>().mFetchParentPromise->IsResolved()) {
// the parent promise resolution leads to deleting of actors
// mActorDying prevents further access to FetchParent
mActorDying = true;
mArgs.as<WorkerFetchArgs>().mFetchParentPromise->Resolve(true,
__func__);
}
}
}
return;
}
mFetchDriver->RunAbortAlgorithm();
return;
}
@@ -367,6 +399,11 @@ void FetchService::FetchInstance::OnResponseEnd(
MOZ_ASSERT(mPromises);
if (mArgs.is<WorkerFetchArgs>() &&
mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Exists()) {
mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Complete();
}
if (aReason == eAborted) {
// If ResponseAvailablePromise has not resolved yet, resolved with
// NS_ERROR_DOM_ABORT_ERR response.
@@ -416,7 +453,7 @@ void FetchService::FetchInstance::OnResponseAvailableInternal(
this, body.get()));
MOZ_ASSERT(mRequest);
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[response = mResponse.clonePtr(), actorID = GetActorID()]() mutable {
@@ -457,7 +494,7 @@ void FetchService::FetchInstance::OnDataAvailable() {
MOZ_ASSERT(mRequest);
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction(__func__, [actorID = GetActorID()]() {
FETCH_LOG(("FetchInstance::OnDataAvailable, Runnable"));
@@ -474,7 +511,7 @@ void FetchService::FetchInstance::OnDataAvailable() {
void FetchService::FetchInstance::FlushConsoleReport() {
FETCH_LOG(("FetchInstance::FlushConsoleReport [%p]", this));
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
if (!mReporter) {
return;
}
@@ -517,7 +554,7 @@ void FetchService::FetchInstance::OnReportPerformanceTiming() {
// Force replace initiatorType for ServiceWorkerNavgationPreload.
if (mArgsType == FetchArgsType::NavigationPreload) {
timing.initiatorType() = u"navigation"_ns;
} else {
} else if (!mActorDying) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [actorID = GetActorID(), timing = timing]() {
FETCH_LOG(("FetchInstance::OnReportPerformanceTiming, Runnable"));
@@ -796,7 +833,7 @@ NS_IMETHODIMP FetchService::Observe(nsISupports* aSubject, const char* aTopic,
if (res) {
return false;
}
entry.Data()->Cancel();
entry.Data()->Cancel(true);
return true;
});
}
@@ -848,7 +885,8 @@ RefPtr<FetchServicePromises> FetchService::Fetch(FetchArgs&& aArgs) {
return promises;
}
void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises) {
void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises,
bool aForceAbort) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromises);
@@ -858,11 +896,22 @@ void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises) {
if (entry) {
// Notice any modifications here before entry.Remove() probably should be
// reflected to Observe() offline case.
entry.Data()->Cancel();
entry.Data()->Cancel(aForceAbort);
entry.Remove();
FETCH_LOG(
("FetchService::CancelFetch entry [%p] removed", aPromises.get()));
}
}
MozPromiseRequestHolder<FetchServiceResponseEndPromise>&
FetchService::GetResponseEndPromiseHolder(
const RefPtr<FetchServicePromises>& aPromises) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromises);
auto entry = mFetchInstanceTable.Lookup(aPromises);
MOZ_ASSERT(entry);
return entry.Data()->GetResponseEndPromiseHolder();
}
} // namespace mozilla::dom

View File

@@ -104,6 +104,9 @@ class FetchService final : public nsIObserver {
nsCOMPtr<nsISerialEventTarget> mEventTarget;
nsID mActorID;
bool mIsThirdPartyContext;
MozPromiseRequestHolder<FetchServiceResponseEndPromise>
mResponseEndPromiseHolder;
RefPtr<GenericPromise::Private> mFetchParentPromise;
};
// Used for content process main thread fetch()
@@ -144,7 +147,11 @@ class FetchService final : public nsIObserver {
// The created FetchInstance is saved in mFetchInstanceTable
RefPtr<FetchServicePromises> Fetch(FetchArgs&& aArgs);
void CancelFetch(const RefPtr<FetchServicePromises>&& aPromises);
void CancelFetch(const RefPtr<FetchServicePromises>&& aPromises,
bool aForceAbort);
MozPromiseRequestHolder<FetchServiceResponseEndPromise>&
GetResponseEndPromiseHolder(const RefPtr<FetchServicePromises>& aPromises);
private:
/**
@@ -166,10 +173,15 @@ class FetchService final : public nsIObserver {
nsresult Initialize(FetchArgs&& aArgs);
const FetchArgs& Args() { return mArgs; }
MozPromiseRequestHolder<FetchServiceResponseEndPromise>&
GetResponseEndPromiseHolder() {
MOZ_ASSERT(mArgs.is<WorkerFetchArgs>());
return mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder;
}
RefPtr<FetchServicePromises> Fetch();
void Cancel();
void Cancel(bool aForceAbort);
bool IsLocalHostFetch() const;
@@ -198,8 +210,8 @@ class FetchService final : public nsIObserver {
RefPtr<FetchDriver> mFetchDriver;
SafeRefPtr<InternalResponse> mResponse;
RefPtr<FetchServicePromises> mPromises;
FetchArgsType mArgsType;
Atomic<bool, Relaxed> mActorDying{false};
};
~FetchService();

View File

@@ -381,7 +381,7 @@ mozilla::ipc::IPCResult FetchEventOpChild::Recv__delete__(
mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
if (mPreloadResponseReadyPromises) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises));
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises), false);
}
/**