/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// This exists so that [Assert]IsOnBackgroundThread() can continue to work // during shutdown. We can rely on TLS being 0 initialized, so only the // background thread itself needs to ever set this flag. static MOZ_THREAD_LOCAL(bool) sTLSIsOnBackgroundThread;
class ParentImpl final : public BackgroundParentImpl { friendclass ChildImpl; friendclass mozilla::ipc::BackgroundParent; friendclass mozilla::ipc::BackgroundStarterParent;
// The length of time we will wait at shutdown for all actors to clean // themselves up before forcing them to be destroyed. staticconst uint32_t kShutdownTimerDelayMS = 10000;
// This is only modified on the main thread. It is null if the thread does not // exist or is shutting down. static StaticRefPtr<nsIThread> sBackgroundThread;
// This is created and destroyed on the main thread but only modified on the // background thread. It is specific to each instance of sBackgroundThread. static nsTArray<IToplevelProtocol*>* sLiveActorsForBackgroundThread;
// This is only modified on the main thread. static StaticRefPtr<nsITimer> sShutdownTimer;
// Maintains a count of live actors so that the background thread can be shut // down when it is no longer needed. // May be incremented on either the background thread (by an existing actor) // or on the main thread, but must be decremented on the main thread. static Atomic<uint64_t> sLiveActorCount;
// This is only modified on the main thread. It is true after the shutdown // observer is registered and is never unset thereafter. staticbool sShutdownObserverRegistered;
// This is only modified on the main thread. It prevents us from trying to // create the background thread after application shutdown has started. staticbool sShutdownHasStarted;
// null if this is a same-process actor. const RefPtr<ThreadsafeContentParentHandle> mContent;
// Set when the actor is opened successfully and used to handle shutdown // hangs. Only touched on the background thread.
nsTArray<IToplevelProtocol*>* mLiveActorArray;
// Set at construction to indicate whether this parent actor corresponds to a // child actor in another process or to a child actor from a different thread // in the same process. constbool mIsOtherProcessActor;
// Set after ActorDestroy has been called. Only touched on the background // thread. bool mActorDestroyed;
// `ParentImpl` instances need to be deleted on the main thread, despite IPC // controlling them on a background thread. Use `_WITH_DELETE_ON_MAIN_THREAD` // to force destruction to occur on the desired thread.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(ParentImpl,
override)
void Destroy();
private: // Forwarded from BackgroundParent. staticbool IsOtherProcessActor(PBackgroundParent* aBackgroundActor);
// Forwarded from BackgroundParent. static ThreadsafeContentParentHandle* GetContentParentHandle(
PBackgroundParent* aBackgroundActor);
// Forwarded from BackgroundParent. static uint64_t GetChildID(PBackgroundParent* aBackgroundActor);
// Forwarded from BackgroundParent. staticvoid KillHardAsync(PBackgroundParent* aBackgroundActor, const nsACString& aReason);
// NOTE: ParentImpl could be used in 2 cases below. // 1. Within the parent process. // 2. Between parent process and content process. // |aContent| should be not null for case 2. For cases 1, it's null. explicit ParentImpl(ThreadsafeContentParentHandle* aContent, bool aIsOtherProcessActor)
: mContent(aContent),
mLiveActorArray(nullptr),
mIsOtherProcessActor(aIsOtherProcessActor),
mActorDestroyed(false) {
AssertIsInMainProcess();
MOZ_ASSERT_IF(!aIsOtherProcessActor, XRE_IsParentProcess());
}
class ChildImpl final : public BackgroundChildImpl { friendclass mozilla::ipc::BackgroundChild; friendclass mozilla::ipc::BackgroundChildImpl; friendclass mozilla::ipc::BackgroundStarterChild;
private: // A thread-local index that is not valid. static constexpr unsignedint kBadThreadLocalIndex = static_cast<unsignedint>(-1);
// ThreadInfoWrapper encapsulates ThreadLocalInfo and ThreadLocalIndex and // also provides some common functions for creating PBackground IPC actor. class ThreadInfoWrapper final { friendclass ChildImpl;
public: using ActorCreateFunc = void (*)(ThreadLocalInfo*, unsignedint,
nsIEventTarget*, ChildImpl**);
ThreadInfoWrapper() = default;
void Startup() {
MOZ_ASSERT(mThreadLocalIndex == kBadThreadLocalIndex, "ThreadInfoWrapper::Startup() called more than once!");
PRStatus status =
PR_NewThreadPrivateIndex(&mThreadLocalIndex, ThreadLocalDestructor);
MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");
// Create a pair of endpoints and send them to the other process.
Endpoint<PBackgroundStarterParent> parent;
Endpoint<PBackgroundStarterChild> child;
MOZ_ALWAYS_SUCCEEDS(PBackgroundStarter::CreateEndpoints(
aActor->OtherEndpointProcInfo(), EndpointProcInfo::Current(), &parent,
&child));
MOZ_ALWAYS_TRUE(aActor->SendInitBackground(std::move(parent)));
// Swap in the newly initialized `BackgroundStarterChild`, and close the // previous one if we're replacing an existing PBackgroundStarterChild // instance.
RefPtr<BackgroundStarterChild> prevStarter;
{ auto lock = mStarter.Lock();
prevStarter = lock->forget();
*lock = starter.forget();
} if (prevStarter) {
CloseStarter(prevStarter);
}
}
// Clearing the thread local will synchronously close the actor.
DebugOnly<PRStatus> status =
PR_SetThreadPrivate(mThreadLocalIndex, nullptr);
MOZ_ASSERT(status == PR_SUCCESS);
}
PBackgroundChild* GetOrCreateForCurrentThread() { // Processes can be told to do final CC's during shutdown even though // they never finished starting (and thus call this), because they // hadn't gotten far enough to call Startup() before shutdown began. if (mThreadLocalIndex == kBadThreadLocalIndex) {
NS_ERROR("BackgroundChild::Startup() was never called"); return nullptr;
} if (NS_IsMainThread() && ChildImpl::sShutdownHasStarted) { return nullptr;
}
if (threadLocalInfo->mActor) { return threadLocalInfo->mActor;
}
RefPtr<BackgroundStarterChild> starter;
{ auto lock = mStarter.Lock();
starter = *lock;
} if (!starter) {
CRASH_IN_CHILD_PROCESS("No BackgroundStarterChild"); return nullptr;
}
Endpoint<PBackgroundParent> parent;
Endpoint<PBackgroundChild> child;
nsresult rv;
rv = PBackground::CreateEndpoints(starter->mOtherProcInfo,
EndpointProcInfo::Current(), &parent,
&child); if (NS_FAILED(rv)) {
NS_WARNING("Failed to create top level actor!"); return nullptr;
}
RefPtr<ChildImpl> strongActor = new ChildImpl(); if (!child.Bind(strongActor)) {
CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!"); return nullptr;
}
strongActor->SetActorAlive();
threadLocalInfo->mActor = strongActor;
// Dispatch to the background task queue to create the relevant actor in // the remote process.
starter->mTaskQueue->Dispatch(NS_NewRunnableFunction( "PBackground GetOrCreateForCurrentThread",
[starter, endpoint = std::move(parent)]() mutable { if (!starter->SendInitBackground(std::move(endpoint))) {
NS_WARNING("Failed to create toplevel actor");
}
})); return strongActor;
}
// This is only modified on the main thread. It is the thread-local index // that we use to store the BackgroundChild for each thread. unsignedint mThreadLocalIndex = kBadThreadLocalIndex;
// On the main thread, we store TLS in this global instead of in // mThreadLocalIndex. That way, cooperative main threads all share the same // thread info.
ThreadLocalInfo* mMainThreadInfo = nullptr;
// The starter which will be used to launch PBackground instances of this // type. Only modified on the main thread, but may be read by any thread // wanting to start background actors.
StaticDataMutex<StaticRefPtr<BackgroundStarterChild>> mStarter{"mStarter"};
};
// For PBackground between parent and content process. static ThreadInfoWrapper sParentAndContentProcessThreadInfo;
// This is only modified on the main thread. It prevents us from trying to // create the background thread after application shutdown has started. staticbool sShutdownHasStarted;
// This type is threadsafe refcounted as actors managed by it may be destroyed // after the thread it is bound to dies, and hold a reference to this object. // // It is _not_ safe to use this type or any methods on it from off of the // thread it was created for.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChildImpl, override)
private: // Forwarded from BackgroundChild. staticvoid Startup();
// Forwarded from BackgroundChild. static PBackgroundChild* GetForCurrentThread();
// Forwarded from BackgroundChild. static PBackgroundChild* GetOrCreateForCurrentThread();
staticvoid CloseForCurrentThread();
// Forwarded from BackgroundChildImpl. static BackgroundChildImpl::ThreadLocal* GetThreadLocalForCurrentThread();
// Forwarded from BackgroundChild. staticvoid InitContentStarter(mozilla::dom::ContentChild* aContent);
if (RefPtr<ContentParent> contentParent =
handle->GetContentParent()) {
contentParent->KillHard(reason.get());
}
}),
NS_DISPATCH_NORMAL));
// After we've scheduled killing of the remote process, also ensure we induce // a connection error in the IPC channel to immediately stop all IPC // communication on this channel. if (aBackgroundActor->CanSend()) {
aBackgroundActor->GetIPCChannel()->InduceConnectionError();
}
}
if (sLiveActorCount) { // We need to spin the event loop while we wait for all the actors to be // cleaned up. We also set a timeout to force-kill any hanging actors.
TimerCallbackClosure closure(thread, liveActors.get());
if (!liveActors->IsEmpty()) { // Copy the array since calling Close() could mutate the // actual array.
nsTArray<IToplevelProtocol*> actorsToClose(liveActors->Clone()); for (IToplevelProtocol* actor : actorsToClose) {
actor->Close();
}
} return GenericPromise::CreateAndResolve(true, __func__);
})
->Then(GetCurrentSerialEventTarget(), __func__, []() {
MOZ_ASSERT(sLiveActorCount);
sLiveActorCount--;
});
}
void ParentImpl::Destroy() { // May be called on any thread!
if (mLiveActorArray) {
MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
mLiveActorArray = nullptr;
}
// This is tricky. We should be able to call Destroy() here directly because // we're not going to touch 'this' or our MessageChannel any longer on this // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when // it runs it will destroy 'this' and our associated MessageChannel. However, // IPDL is about to call MessageChannel::Clear() on this thread! To avoid // racing with the main thread we must ensure that the MessageChannel lives // long enough to be cleared in this call stack.
if (mLiveActorArray) {
MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
mLiveActorArray = nullptr;
}
// Make sure to decrement `sLiveActorCount` on the main thread.
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
NS_NewRunnableFunction("BackgroundStarterParent::MainThreadDestroy",
[] { ParentImpl::sLiveActorCount--; })));
}
// static void ChildImpl::Startup() { // This happens on the main thread but before XPCOM has started so we can't // assert that we're being called on the main thread here.
// Initialize a starter actor to allow starting PBackground within the parent // process. if (XRE_IsParentProcess()) {
Endpoint<PBackgroundStarterParent> parent;
Endpoint<PBackgroundStarterChild> child;
MOZ_ALWAYS_SUCCEEDS(PBackgroundStarter::CreateEndpoints(
EndpointProcInfo::Current(), EndpointProcInfo::Current(), &parent,
&child));
// static void ChildImpl::CloseForCurrentThread() {
MOZ_ASSERT(!NS_IsMainThread(), "PBackground for the main thread should be shut down via " "ChildImpl::Shutdown().");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.