Bug 1836700 - Remove BusyCount of WorkerPrivate. r=asuth,ipc-reviewers,mccr8

Differential Revision: https://phabricator.services.mozilla.com/D182718
This commit is contained in:
Eden Chuang 2023-11-13 04:00:40 +00:00
parent ce4599d3d4
commit 76d6e6b69c
23 changed files with 346 additions and 300 deletions

View File

@ -1825,7 +1825,7 @@ class WorkerRunnableDispatcher final : public WorkerRunnable {
WorkerRunnableDispatcher(RefPtr<EventSourceImpl>&& aImpl,
WorkerPrivate* aWorkerPrivate,
already_AddRefed<nsIRunnable> aEvent)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, WorkerThread),
mEventSourceImpl(std::move(aImpl)),
mEvent(std::move(aEvent)) {}

View File

@ -96,7 +96,7 @@ class TeardownRunnableOnWorker final : public WorkerControlRunnable,
public:
TeardownRunnableOnWorker(WorkerPrivate* aWorkerPrivate,
BroadcastChannelChild* aActor)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
TeardownRunnable(aActor) {}
bool WorkerRun(JSContext*, WorkerPrivate*) override {

View File

@ -69,6 +69,10 @@ void CacheWorkerRef::AddActor(ActorChild& aActor) {
MOZ_ASSERT(!mActorList.Contains(&aActor));
mActorList.AppendElement(WrapNotNullUnchecked(&aActor));
if (mBehavior == eIPCWorkerRef) {
MOZ_ASSERT(mIPCWorkerRef);
mIPCWorkerRef->SetActorCount(mActorList.Length());
}
// Allow an actor to be added after we've entered the Notifying case. We
// can't stop the actor creation from racing with out destruction of the
@ -90,6 +94,11 @@ void CacheWorkerRef::RemoveActor(ActorChild& aActor) {
MOZ_ASSERT(!mActorList.Contains(&aActor));
if (mBehavior == eIPCWorkerRef) {
MOZ_ASSERT(mIPCWorkerRef);
mIPCWorkerRef->SetActorCount(mActorList.Length());
}
if (mActorList.IsEmpty()) {
mStrongWorkerRef = nullptr;
mIPCWorkerRef = nullptr;

View File

@ -312,7 +312,6 @@ class NotificationWorkerRunnable : public MainThreadWorkerRunnable {
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnWorkerThread();
aWorkerPrivate->ModifyBusyCountFromWorker(true);
// WorkerScope might start dying at the moment. And WorkerRunInternal()
// should not be executed once WorkerScope is dying, since
// WorkerRunInternal() might access resources which already been freed
@ -324,11 +323,6 @@ class NotificationWorkerRunnable : public MainThreadWorkerRunnable {
return true;
}
void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
aWorkerPrivate->ModifyBusyCountFromWorker(false);
}
virtual void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) = 0;
};
@ -361,7 +355,6 @@ class ReleaseNotificationRunnable final : public NotificationWorkerRunnable {
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnWorkerThread();
aWorkerPrivate->ModifyBusyCountFromWorker(true);
// ReleaseNotificationRunnable is only used in StrongWorkerRef's shutdown
// callback. At the moment, it is supposed to executing
// mNotification->ReleaseObject() safely even though the corresponding

View File

@ -39,7 +39,7 @@ class PerformanceEntryAdder final : public WorkerControlRunnable {
PerformanceEntryAdder(WorkerPrivate* aWorkerPrivate,
PerformanceStorageWorker* aStorage,
UniquePtr<PerformanceProxyData>&& aData)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mStorage(aStorage),
mData(std::move(aData)) {}

View File

@ -776,8 +776,7 @@ class PromiseWorkerProxyRunnable : public WorkerRunnable {
public:
PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
PromiseWorkerProxy::RunCallbackFunc aFunc)
: WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(), WorkerThread),
mPromiseWorkerProxy(aPromiseWorkerProxy),
mFunc(aFunc) {
MOZ_ASSERT(NS_IsMainThread());

View File

@ -277,7 +277,7 @@ class ServiceWorkerOp::ServiceWorkerOpRunnable : public WorkerDebuggeeRunnable {
ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
WorkerPrivate* aWorkerPrivate)
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThread),
mOwner(std::move(aOwner)) {
AssertIsOnMainThread();
MOZ_ASSERT(mOwner);
@ -856,10 +856,7 @@ class NotificationEventOp : public ExtendableEventOp,
timer.swap(mTimer);
// We swap first and then initialize the timer so that even if initializing
// fails, we still clean the busy count and interaction count correctly.
// The timer can't be initialized before modyfing the busy count since the
// timer thread could run and call the timeout but the worker may
// already be terminating and modifying the busy count could fail.
// fails, we still clean the interaction count correctly.
uint32_t delay = mArgs.get_ServiceWorkerNotificationEventOpArgs()
.disableOpenClickDelay();
rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);

View File

@ -2740,7 +2740,7 @@ class WorkerRunnableDispatcher final : public WorkerRunnable {
WorkerRunnableDispatcher(WebSocketImpl* aImpl,
ThreadSafeWorkerRef* aWorkerRef,
already_AddRefed<nsIRunnable> aEvent)
: WorkerRunnable(aWorkerRef->Private(), WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerRef->Private(), WorkerThread),
mWebSocketImpl(aImpl),
mEvent(std::move(aEvent)) {}

View File

@ -33,7 +33,7 @@
namespace mozilla::dom {
EventWithOptionsRunnable::EventWithOptionsRunnable(Worker& aWorker)
: WorkerDebuggeeRunnable(aWorker.mWorkerPrivate,
WorkerRunnable::WorkerThreadModifyBusyCount),
WorkerRunnable::WorkerThread),
StructuredCloneHolder(CloningSupported, TransferringSupported,
StructuredCloneScope::SameProcess) {}
@ -131,7 +131,7 @@ bool EventWithOptionsRunnable::BuildAndFireEvent(
bool EventWithOptionsRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) {
if (mBehavior == ParentThreadUnchangedBusyCount) {
if (mTarget == ParentThread) {
// Don't fire this event if the JS object has been disconnected from the
// private object.
if (!aWorkerPrivate->IsAcceptingEvents()) {

View File

@ -15,8 +15,8 @@
namespace mozilla::dom {
MessageEventRunnable::MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
: WorkerDebuggeeRunnable(aWorkerPrivate, aBehavior),
Target aTarget)
: WorkerDebuggeeRunnable(aWorkerPrivate, aTarget),
StructuredCloneHolder(CloningSupported, TransferringSupported,
StructuredCloneScope::SameProcess) {}
@ -84,7 +84,7 @@ bool MessageEventRunnable::DispatchDOMEvent(JSContext* aCx,
bool MessageEventRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) {
if (mBehavior == ParentThreadUnchangedBusyCount) {
if (mTarget == ParentThread) {
// Don't fire this event if the JS object has been disconnected from the
// private object.
if (!aWorkerPrivate->IsAcceptingEvents()) {

View File

@ -20,8 +20,7 @@ namespace dom {
class MessageEventRunnable final : public WorkerDebuggeeRunnable,
public StructuredCloneHolder {
public:
MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior);
MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget);
bool DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
DOMEventTargetHelper* aTarget, bool aIsMainThread);

View File

@ -602,8 +602,7 @@ class JSDispatchableRunnable final : public WorkerRunnable {
public:
JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
JS::Dispatchable* aDispatchable)
: WorkerRunnable(aWorkerPrivate,
WorkerRunnable::WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, WorkerRunnable::WorkerThread),
mDispatchable(aDispatchable) {
MOZ_ASSERT(mDispatchable);
}
@ -1461,7 +1460,7 @@ namespace {
class DumpCrashInfoRunnable : public WorkerControlRunnable {
public:
explicit DumpCrashInfoRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mMonitor("DumpCrashInfoRunnable::mMonitor") {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
@ -1525,12 +1524,7 @@ struct ActiveWorkerStats {
new DumpCrashInfoRunnable(worker);
if (runnable->DispatchAndWait()) {
++(this->*Category);
// BC: Busy Count
mMessage.AppendPrintf("-BC:%d", worker->BusyCount());
mMessage.Append(runnable->MsgData());
} else {
mMessage.AppendPrintf("-BC:%d DispatchFailed", worker->BusyCount());
}
}
}
@ -1985,8 +1979,6 @@ void LogWorker(WorkerPrivate* worker, const char* category) {
worker->GetLoadingPrincipal()->GetOrigin(loadingOrigin);
SHUTDOWN_LOG(("LoadingPrincipal: %s", loadingOrigin.get()));
SHUTDOWN_LOG(("BusyCount: %d", worker->BusyCount()));
RefPtr<DumpCrashInfoRunnable> runnable = new DumpCrashInfoRunnable(worker);
if (runnable->DispatchAndWait()) {
SHUTDOWN_LOG(("CrashInfo: %s", runnable->MsgData().get()));

View File

@ -119,8 +119,8 @@ void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
"Worker.postMessage", nameOrScriptURL.get(),
JS::ProfilingCategoryPair::DOM, flags);
RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
mWorkerPrivate, WorkerRunnable::WorkerThreadModifyBusyCount);
RefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(mWorkerPrivate, WorkerRunnable::WorkerThread);
JS::CloneDataPolicy clonePolicy;
// DedicatedWorkers are always part of the same agent cluster.

View File

@ -34,7 +34,7 @@ class WrappedControlRunnable final : public WorkerControlRunnable {
public:
WrappedControlRunnable(WorkerPrivate* aWorkerPrivate,
nsCOMPtr<nsIRunnable>&& aInner)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mInner(std::move(aInner)) {}
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {

View File

@ -61,6 +61,8 @@
#include "mozilla/dom/WindowContext.h"
#include "mozilla/extensions/ExtensionBrowser.h" // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "mozilla/Telemetry.h"
@ -182,7 +184,7 @@ class ExternalRunnableWrapper final : public WorkerRunnable {
public:
ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
nsIRunnable* aWrappedRunnable)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, WorkerThread),
mWrappedRunnable(aWrappedRunnable) {
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aWrappedRunnable);
@ -245,7 +247,7 @@ class WorkerFinishedRunnable final : public WorkerControlRunnable {
public:
WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
WorkerPrivate* aFinishedWorker)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mFinishedWorker(aFinishedWorker) {
aFinishedWorker->IncreaseWorkerFinishedRunnableCount();
}
@ -322,31 +324,6 @@ class TopLevelWorkerFinishedRunnable final : public Runnable {
}
};
class ModifyBusyCountRunnable final : public WorkerControlRunnable {
bool mIncrease;
public:
ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
: WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
mIncrease(aIncrease) {}
private:
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->ModifyBusyCount(mIncrease);
}
virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
if (mIncrease) {
WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
return;
}
// Don't do anything here as it's possible that aWorkerPrivate has been
// deleted.
}
};
class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
nsString mScriptURL;
const mozilla::Encoding* mDocumentEncoding;
@ -357,7 +334,7 @@ class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
UniquePtr<SerializedStackHolder> aOriginStack,
const nsAString& aScriptURL,
const mozilla::Encoding* aDocumentEncoding)
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThread),
mScriptURL(aScriptURL),
mDocumentEncoding(aDocumentEncoding),
mOriginStack(aOriginStack.release()) {}
@ -464,8 +441,7 @@ class NotifyRunnable final : public WorkerControlRunnable {
public:
NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mStatus(aStatus) {
: WorkerControlRunnable(aWorkerPrivate, WorkerThread), mStatus(aStatus) {
MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
aStatus == Killing);
}
@ -473,34 +449,27 @@ class NotifyRunnable final : public WorkerControlRunnable {
private:
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnParentThread();
return aWorkerPrivate->ModifyBusyCount(true);
return true;
}
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override {
aWorkerPrivate->AssertIsOnParentThread();
if (!aDispatchResult) {
// We couldn't dispatch to the worker, which means it's already dead.
// Undo the busy count modification.
aWorkerPrivate->ModifyBusyCount(false);
}
}
virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {
aWorkerPrivate->ModifyBusyCountFromWorker(false);
}
virtual bool WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) override {
return aWorkerPrivate->NotifyInternal(mStatus);
}
virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aRunResult) override {}
};
class FreezeRunnable final : public WorkerControlRunnable {
public:
explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread) {}
private:
virtual bool WorkerRun(JSContext* aCx,
@ -512,7 +481,7 @@ class FreezeRunnable final : public WorkerControlRunnable {
class ThawRunnable final : public WorkerControlRunnable {
public:
explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread) {}
private:
virtual bool WorkerRun(JSContext* aCx,
@ -526,7 +495,7 @@ class PropagateStorageAccessPermissionGrantedRunnable final
public:
explicit PropagateStorageAccessPermissionGrantedRunnable(
WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread) {}
private:
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
@ -567,7 +536,7 @@ class ReportErrorToConsoleRunnable final : public WorkerRunnable {
ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
const char* aMessage,
const nsTArray<nsString>& aParams)
: WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, ParentThread),
mMessage(aMessage),
mParams(aParams.Clone()) {}
@ -595,7 +564,7 @@ class TimerRunnable final : public WorkerRunnable,
NS_DECL_ISUPPORTS_INHERITED
explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerRunnable(aWorkerPrivate, WorkerThread) {}
private:
~TimerRunnable() = default;
@ -637,8 +606,7 @@ class DebuggerImmediateRunnable : public WorkerRunnable {
public:
explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
dom::Function& aHandler)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mHandler(&aHandler) {}
: WorkerRunnable(aWorkerPrivate, WorkerThread), mHandler(&aHandler) {}
private:
virtual bool IsDebuggerRunnable() const override { return true; }
@ -698,7 +666,7 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
public:
UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
const JS::ContextOptions& aContextOptions)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mContextOptions(aContextOptions) {}
private:
@ -733,7 +701,7 @@ class UpdateJSWorkerMemoryParameterRunnable final
UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
JSGCParamKey aKey,
Maybe<uint32_t> aValue)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mValue(aValue),
mKey(aKey) {}
@ -753,7 +721,7 @@ class UpdateGCZealRunnable final : public WorkerControlRunnable {
public:
UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
uint32_t aFrequency)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mGCZeal(aGCZeal),
mFrequency(aFrequency) {}
@ -771,8 +739,7 @@ class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
public:
SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mState(aState) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread), mState(aState) {}
private:
virtual bool WorkerRun(JSContext* aCx,
@ -789,7 +756,7 @@ class GarbageCollectRunnable final : public WorkerControlRunnable {
public:
GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
bool aCollectChildren)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mShrinking(aShrinking),
mCollectChildren(aCollectChildren) {}
@ -823,7 +790,7 @@ class CycleCollectRunnable : public WorkerControlRunnable {
public:
CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mCollectChildren(aCollectChildren) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
@ -849,7 +816,7 @@ class OfflineStatusChangeRunnable : public WorkerRunnable {
class MemoryPressureRunnable : public WorkerControlRunnable {
public:
explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->MemoryPressureInternal();
@ -880,8 +847,7 @@ PRThread* PRThreadFromThread(nsIThread* aThread) {
class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable {
public:
explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerDebuggeeRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {
}
: WorkerDebuggeeRunnable(aWorkerPrivate, ParentThread) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->Cancel();
@ -894,7 +860,7 @@ class CancelingWithTimeoutOnParentRunnable final
: public WorkerControlRunnable {
public:
explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, ParentThread) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
aWorkerPrivate->AssertIsOnParentThread();
@ -1558,12 +1524,14 @@ void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
// The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
// Worker object, which is really held by the worker thread. We traverse this
// reference if and only if our busy count is zero and we have not released
// the main thread reference. We do not unlink it. This allows the CC to
// break cycles involving the Worker and begin shutting it down (which does
// happen in unlink) but ensures that the WorkerPrivate won't be deleted
// before we're done shutting down the thread.
if (!mBusyCount && !mMainThreadObjectsForgotten) {
// reference if and only if all main thread event queues are empty, no
// shutdown tasks, no StrongWorkerRefs, no child workers, no timeouts, no
// blocking background actors, and we have not released the main thread
// reference. We do not unlink it. This allows the CC to break cycles
// involving the Worker and begin shutting it down (which does happen in
// unlink) but ensures that the WorkerPrivate won't be deleted before we're
// done shutting down the thread.
if (IsEligibleForCC() && !mMainThreadObjectsForgotten) {
nsCycleCollectionTraversalCallback& cb = aCb;
WorkerPrivate* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
@ -1944,31 +1912,6 @@ bool WorkerPrivate::Close() {
return true;
}
bool WorkerPrivate::ModifyBusyCount(bool aIncrease) {
AssertIsOnParentThread();
MOZ_ASSERT(aIncrease || mBusyCount, "Mismatched busy count mods!");
if (aIncrease) {
mBusyCount++;
return true;
}
if (--mBusyCount == 0) {
bool shouldCancel;
{
MutexAutoLock lock(mMutex);
shouldCancel = mParentStatus == Canceling;
}
if (shouldCancel && !Cancel()) {
return false;
}
}
return true;
}
bool WorkerPrivate::ProxyReleaseMainThreadObjects() {
AssertIsOnParentThread();
MOZ_ASSERT(!mMainThreadObjectsForgotten);
@ -2289,6 +2232,7 @@ WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible(
WorkerPrivate* const aParent)
: mNumWorkerRefsPreventingShutdownStart(0),
mDebuggerEventLoopLevel(0),
mNonblockingCCBackgroundActorCount(0),
mErrorHandlerRecursionCount(0),
mNextTimeoutId(1),
mCurrentTimerNestingLevel(0),
@ -2362,7 +2306,6 @@ WorkerPrivate::WorkerPrivate(
new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
mParentStatus(Pending),
mStatus(Pending),
mBusyCount(0),
mCreationTimeStamp(TimeStamp::Now()),
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
mReportedUseCounters(false),
@ -3393,6 +3336,13 @@ void WorkerPrivate::DoRunLoop(JSContext* aCx) {
}
}
// Checking the background actors if needed, any runnable execution could
// release background actors which blocks GC/CC on
// WorkerPrivate::mParentEventTargetRef.
if (currentStatus < Canceling) {
UpdateCCFlag(CCFlag::CheckBackgroundActors);
}
if (!debuggerRunnablesPending && !normalRunnablesPending) {
// Both the debugger event queue and the normal event queue has been
// exhausted, cancel the periodic GC timer and schedule the idle GC timer.
@ -4120,24 +4070,6 @@ void WorkerPrivate::UnlinkTimeouts() {
data->mTimeouts.Clear();
}
bool WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease) {
AssertIsOnWorkerThread();
{
MutexAutoLock lock(mMutex);
// If we're in shutdown then the busy count is no longer being considered so
// just return now.
if (mStatus >= Killing) {
return true;
}
}
RefPtr<ModifyBusyCountRunnable> runnable =
new ModifyBusyCountRunnable(this, aIncrease);
return runnable->Dispatch();
}
bool WorkerPrivate::AddChildWorker(WorkerPrivate& aChildWorker) {
auto data = mWorkerThreadAccessible.Access();
@ -4157,8 +4089,11 @@ bool WorkerPrivate::AddChildWorker(WorkerPrivate& aChildWorker) {
"Already know about this one!");
data->mChildWorkers.AppendElement(&aChildWorker);
return data->mChildWorkers.Length() == 1 ? ModifyBusyCountFromWorker(true)
: true;
if (data->mChildWorkers.Length() == 1) {
UpdateCCFlag(CCFlag::IneligibleForChildWorker);
}
return true;
}
void WorkerPrivate::RemoveChildWorker(WorkerPrivate& aChildWorker) {
@ -4168,8 +4103,8 @@ void WorkerPrivate::RemoveChildWorker(WorkerPrivate& aChildWorker) {
"Didn't know about this one!");
data->mChildWorkers.RemoveElement(&aChildWorker);
if (data->mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
NS_WARNING("Failed to modify busy count!");
if (data->mChildWorkers.IsEmpty()) {
UpdateCCFlag(CCFlag::EligibleForChildWorker);
}
}
@ -4204,11 +4139,10 @@ bool WorkerPrivate::AddWorkerRef(WorkerRef* aWorkerRef,
"Already know about this one!");
if (aWorkerRef->IsPreventingShutdown()) {
if (!data->mNumWorkerRefsPreventingShutdownStart &&
!ModifyBusyCountFromWorker(true)) {
return false;
}
data->mNumWorkerRefsPreventingShutdownStart += 1;
if (data->mNumWorkerRefsPreventingShutdownStart == 1) {
UpdateCCFlag(CCFlag::IneligibleForWorkerRef);
}
}
data->mWorkerRefs.AppendElement(aWorkerRef);
@ -4227,9 +4161,8 @@ void WorkerPrivate::RemoveWorkerRef(WorkerRef* aWorkerRef) {
if (aWorkerRef->IsPreventingShutdown()) {
data->mNumWorkerRefsPreventingShutdownStart -= 1;
if (!data->mNumWorkerRefsPreventingShutdownStart &&
!ModifyBusyCountFromWorker(false)) {
NS_WARNING("Failed to modify busy count!");
if (!data->mNumWorkerRefsPreventingShutdownStart) {
UpdateCCFlag(CCFlag::EligibleForWorkerRef);
}
}
}
@ -4304,6 +4237,120 @@ void WorkerPrivate::RunShutdownTasks() {
mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
}
void WorkerPrivate::AdjustNonblockingCCBackgroundActorCount(int32_t aCount) {
AssertIsOnWorkerThread();
auto data = mWorkerThreadAccessible.Access();
LOGV(("WorkerPrivate::AdjustNonblockingCCBackgroundActors [%p] (%d/%u)", this,
aCount, data->mNonblockingCCBackgroundActorCount));
#ifdef DEBUG
if (aCount < 0) {
MOZ_ASSERT(data->mNonblockingCCBackgroundActorCount >=
(uint32_t)abs(aCount));
}
#endif
data->mNonblockingCCBackgroundActorCount += aCount;
}
void WorkerPrivate::UpdateCCFlag(const CCFlag aFlag) {
LOGV(("WorkerPrivate::UpdateCCFlag [%p]", this));
AssertIsOnWorkerThread();
auto data = mWorkerThreadAccessible.Access();
#ifdef DEBUG
switch (aFlag) {
case CCFlag::EligibleForWorkerRef: {
MOZ_ASSERT(!data->mNumWorkerRefsPreventingShutdownStart);
break;
}
case CCFlag::IneligibleForWorkerRef: {
MOZ_ASSERT(data->mNumWorkerRefsPreventingShutdownStart);
break;
}
case CCFlag::EligibleForChildWorker: {
MOZ_ASSERT(data->mChildWorkers.IsEmpty());
break;
}
case CCFlag::IneligibleForChildWorker: {
MOZ_ASSERT(!data->mChildWorkers.IsEmpty());
break;
}
case CCFlag::EligibleForTimeout: {
MOZ_ASSERT(data->mTimeouts.IsEmpty());
break;
}
case CCFlag::IneligibleForTimeout: {
MOZ_ASSERT(!data->mTimeouts.IsEmpty());
break;
}
case CCFlag::CheckBackgroundActors: {
break;
}
}
#endif
{
MutexAutoLock lock(mMutex);
if (mStatus > Canceling) {
mCCFlagSaysEligible = true;
return;
}
}
auto HasBackgroundActors = [nonblockingActorCount =
data->mNonblockingCCBackgroundActorCount]() {
RefPtr<PBackgroundChild> backgroundChild =
BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(backgroundChild);
auto totalCount = backgroundChild->AllManagedActorsCount();
LOGV(("WorkerPrivate::UpdateCCFlag HasBackgroundActors: %s(%u/%u)",
totalCount > nonblockingActorCount ? "true" : "false", totalCount,
nonblockingActorCount));
return totalCount > nonblockingActorCount;
};
bool eligibleForCC = data->mChildWorkers.IsEmpty() &&
data->mTimeouts.IsEmpty() &&
!data->mNumWorkerRefsPreventingShutdownStart;
// Only checking BackgroundActors when no strong WorkerRef, ChildWorker, and
// Timeout since the checking is expensive.
if (eligibleForCC) {
eligibleForCC = !HasBackgroundActors();
}
{
MutexAutoLock lock(mMutex);
mCCFlagSaysEligible = eligibleForCC;
}
}
bool WorkerPrivate::IsEligibleForCC() {
LOGV(("WorkerPrivate::IsEligibleForCC [%p]", this));
MutexAutoLock lock(mMutex);
if (mStatus > Canceling) {
return true;
}
bool HasShutdownTasks = !mShutdownTasks.IsEmpty();
LOGV(("mMainThreadEventTarget: %s",
mMainThreadEventTarget->IsEmpty() ? "empty" : "non-empty"));
LOGV(("mMainThreadEventTargetForMessaging: %s",
mMainThreadEventTargetForMessaging->IsEmpty() ? "empty" : "non-empty"));
LOGV(("mMainThreadDebuggerEventTarget: %s",
mMainThreadDebuggeeEventTarget->IsEmpty() ? "empty" : "non-empty"));
LOGV(("mCCFlagSaysEligible: %s", mCCFlagSaysEligible ? "true" : "false"));
LOGV(("HasShutdownTasks: %s", HasShutdownTasks ? "true" : "false"));
return mMainThreadEventTarget->IsEmpty() &&
mMainThreadEventTargetForMessaging->IsEmpty() &&
mMainThreadDebuggeeEventTarget->IsEmpty() && mCCFlagSaysEligible &&
!HasShutdownTasks;
}
void WorkerPrivate::CancelAllTimeouts() {
auto data = mWorkerThreadAccessible.Access();
@ -4326,7 +4373,7 @@ void WorkerPrivate::CancelAllTimeouts() {
// them. Otherwise, we need to clean them up ourselves.
if (!data->mRunningExpiredTimeouts) {
data->mTimeouts.Clear();
ModifyBusyCountFromWorker(false);
UpdateCCFlag(CCFlag::EligibleForTimeout);
}
// Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
@ -4711,8 +4758,8 @@ void WorkerPrivate::PostMessageToParent(
return;
}
RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
this, WorkerRunnable::ParentThreadUnchangedBusyCount);
RefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(this, WorkerRunnable::ParentThread);
JS::CloneDataPolicy clonePolicy;
@ -5095,10 +5142,7 @@ int32_t WorkerPrivate::SetTimeout(JSContext* aCx, TimeoutHandler* aHandler,
}
if (!data->mTimerRunning) {
if (!ModifyBusyCountFromWorker(true)) {
aRv.Throw(NS_ERROR_FAILURE);
return 0;
}
UpdateCCFlag(CCFlag::IneligibleForTimeout);
data->mTimerRunning = true;
}
@ -5279,9 +5323,7 @@ bool WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) {
// Either signal the parent that we're no longer using timeouts or reschedule
// the timer.
if (data->mTimeouts.IsEmpty()) {
if (!ModifyBusyCountFromWorker(false)) {
retval = false;
}
UpdateCCFlag(CCFlag::EligibleForTimeout);
data->mTimerRunning = false;
} else if (retval && !RescheduleTimeoutTimer(aCx)) {
retval = false;
@ -5327,9 +5369,12 @@ bool WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) {
void WorkerPrivate::StartCancelingTimer() {
AssertIsOnParentThread();
auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; });
// return if mCancelingTimer has already existed.
if (mCancelingTimer) {
return;
}
MOZ_ASSERT(!mCancelingTimer);
auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; });
if (WorkerPrivate* parent = GetParent()) {
mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());

View File

@ -365,8 +365,6 @@ class WorkerPrivate final
void UnlinkTimeouts();
bool ModifyBusyCountFromWorker(bool aIncrease);
bool AddChildWorker(WorkerPrivate& aChildWorker);
void RemoveChildWorker(WorkerPrivate& aChildWorker);
@ -647,12 +645,6 @@ class WorkerPrivate final
mParentEventTargetRef = aParentEventTargetRef;
}
bool ModifyBusyCount(bool aIncrease);
// This method is used by RuntimeService to know what is going wrong the
// shutting down.
uint32_t BusyCount() { return mBusyCount; }
// Check whether this worker is a secure context. For use from the parent
// thread only; the canonical "is secure context" boolean is stored on the
// compartment of the worker global. The only reason we don't
@ -1141,6 +1133,35 @@ class WorkerPrivate final
return data->mCancelBeforeWorkerScopeConstructed;
}
enum class CCFlag : uint8_t {
EligibleForWorkerRef,
IneligibleForWorkerRef,
EligibleForChildWorker,
IneligibleForChildWorker,
EligibleForTimeout,
IneligibleForTimeout,
CheckBackgroundActors,
};
// When create/release a StrongWorkerRef, child worker, and timeout, this
// method is used to setup if mParentEventTargetRef can get into
// cycle-collection.
// When this method is called, it will also checks if any background actor
// should block the mParentEventTargetRef cycle-collection when there is no
// StrongWorkerRef/ChildWorker/Timeout.
// Worker thread only.
void UpdateCCFlag(const CCFlag);
// This is used in WorkerPrivate::Traverse() to checking if
// mParentEventTargetRef should get into cycle-collection.
// Parent thread only method.
bool IsEligibleForCC();
// A method which adjusts the count of background actors which should not
// block WorkerPrivate::mParentEventTargetRef cycle-collection.
// Worker thread only.
void AdjustNonblockingCCBackgroundActorCount(int32_t aCount);
private:
WorkerPrivate(
WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
@ -1308,7 +1329,7 @@ class WorkerPrivate final
// The worker is owned by its thread, which is represented here. This is set
// in Constructor() and emptied by WorkerFinishedRunnable, and conditionally
// traversed by the cycle collector if the busy count is zero.
// traversed by the cycle collector if no other things preventing shutdown.
//
// There are 4 ways a worker can be terminated:
// 1. GC/CC - When the worker is in idle state (busycount == 0), it allows to
@ -1400,10 +1421,6 @@ class WorkerPrivate final
WorkerStatus mParentStatus MOZ_GUARDED_BY(mMutex);
WorkerStatus mStatus MOZ_GUARDED_BY(mMutex);
// This is touched on parent thread only, but it can be read on a different
// thread before crashing because hanging.
Atomic<uint64_t> mBusyCount;
TimeStamp mCreationTimeStamp;
DOMHighResTimeStamp mCreationTimeHighRes;
@ -1464,6 +1481,11 @@ class WorkerPrivate final
uint32_t mNumWorkerRefsPreventingShutdownStart;
uint32_t mDebuggerEventLoopLevel;
// This is the count of background actors that binding with IPCWorkerRefs.
// This count would be used in WorkerPrivate::UpdateCCFlag for checking if
// CC should be blocked by background actors.
uint32_t mNonblockingCCBackgroundActorCount;
uint32_t mErrorHandlerRecursionCount;
int32_t mNextTimeoutId;
@ -1584,6 +1606,8 @@ class WorkerPrivate final
nsTArray<nsCOMPtr<nsITargetShutdownTask>> mShutdownTasks
MOZ_GUARDED_BY(mMutex);
bool mShutdownTasksRun MOZ_GUARDED_BY(mMutex) = false;
bool mCCFlagSaysEligible MOZ_GUARDED_BY(mMutex){true};
};
class AutoSyncLoopHolder {

View File

@ -20,7 +20,7 @@ class ReleaseRefControlRunnable final : public WorkerControlRunnable {
public:
ReleaseRefControlRunnable(WorkerPrivate* aWorkerPrivate,
already_AddRefed<StrongWorkerRef> aRef)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerControlRunnable(aWorkerPrivate, WorkerThread),
mRef(std::move(aRef)) {
MOZ_ASSERT(mRef);
}
@ -229,20 +229,34 @@ already_AddRefed<IPCWorkerRef> IPCWorkerRef::Create(
if (!ref->HoldWorker(Canceling)) {
return nullptr;
}
ref->mWorkerPrivate->AdjustNonblockingCCBackgroundActorCount(1);
ref->mCallback = std::move(aCallback);
return ref.forget();
}
IPCWorkerRef::IPCWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName)
: WorkerRef(aWorkerPrivate, aName, false) {}
: WorkerRef(aWorkerPrivate, aName, false), mActorCount(1) {}
IPCWorkerRef::~IPCWorkerRef() = default;
IPCWorkerRef::~IPCWorkerRef() {
NS_ASSERT_OWNINGTHREAD(IPCWorkerRef);
// explicit type convertion to avoid undefined behavior of uint32_t overflow.
mWorkerPrivate->AdjustNonblockingCCBackgroundActorCount(
(int32_t)-mActorCount);
ReleaseWorker();
};
WorkerPrivate* IPCWorkerRef::Private() const {
NS_ASSERT_OWNINGTHREAD(IPCWorkerRef);
return mWorkerPrivate;
}
void IPCWorkerRef::SetActorCount(uint32_t aCount) {
NS_ASSERT_OWNINGTHREAD(IPCWorkerRef);
// explicit type convertion to avoid undefined behavior of uint32_t overflow.
mWorkerPrivate->AdjustNonblockingCCBackgroundActorCount((int32_t)aCount -
(int32_t)mActorCount);
mActorCount = aCount;
}
} // namespace mozilla::dom

View File

@ -209,9 +209,14 @@ class IPCWorkerRef final : public WorkerRef {
WorkerPrivate* Private() const;
void SetActorCount(uint32_t aCount);
private:
IPCWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName);
~IPCWorkerRef();
// The count of background actors which binding with this IPCWorkerRef.
uint32_t mActorCount;
};
// Template class to keep an Actor pointer, as a raw pointer, in a ref-counted

View File

@ -53,10 +53,9 @@ const nsIID kWorkerRunnableIID = {
} // namespace
#ifdef DEBUG
WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget)
: mWorkerPrivate(aWorkerPrivate),
mBehavior(aBehavior),
mTarget(aTarget),
mCallingCancelWithinRun(false) {
LOG(("WorkerRunnable::WorkerRunnable [%p]", this));
MOZ_ASSERT(aWorkerPrivate);
@ -77,13 +76,12 @@ bool WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
#ifdef DEBUG
MOZ_ASSERT(aWorkerPrivate);
switch (mBehavior) {
case ParentThreadUnchangedBusyCount:
switch (mTarget) {
case ParentThread:
aWorkerPrivate->AssertIsOnWorkerThread();
break;
case WorkerThreadModifyBusyCount:
case WorkerThreadUnchangedBusyCount:
case WorkerThread:
aWorkerPrivate->AssertIsOnParentThread();
break;
@ -91,11 +89,6 @@ bool WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
}
#endif
if (mBehavior == WorkerThreadModifyBusyCount) {
return aWorkerPrivate->ModifyBusyCount(true);
}
return true;
}
@ -112,8 +105,7 @@ bool WorkerRunnable::DispatchInternal() {
LOG(("WorkerRunnable::DispatchInternal [%p]", this));
RefPtr<WorkerRunnable> runnable(this);
if (mBehavior == WorkerThreadModifyBusyCount ||
mBehavior == WorkerThreadUnchangedBusyCount) {
if (mTarget == WorkerThread) {
if (IsDebuggerRunnable()) {
return NS_SUCCEEDED(
mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget()));
@ -122,7 +114,7 @@ bool WorkerRunnable::DispatchInternal() {
}
}
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
MOZ_ASSERT(mTarget == ParentThread);
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
@ -143,16 +135,12 @@ void WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
MOZ_ASSERT(aWorkerPrivate);
#ifdef DEBUG
switch (mBehavior) {
case ParentThreadUnchangedBusyCount:
switch (mTarget) {
case ParentThread:
aWorkerPrivate->AssertIsOnWorkerThread();
break;
case WorkerThreadModifyBusyCount:
aWorkerPrivate->AssertIsOnParentThread();
break;
case WorkerThreadUnchangedBusyCount:
case WorkerThread:
aWorkerPrivate->AssertIsOnParentThread();
break;
@ -160,12 +148,6 @@ void WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
}
#endif
if (!aDispatchResult) {
if (mBehavior == WorkerThreadModifyBusyCount) {
aWorkerPrivate->ModifyBusyCount(false);
}
}
}
bool WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate) { return true; }
@ -176,16 +158,12 @@ void WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
MOZ_ASSERT(aWorkerPrivate);
#ifdef DEBUG
switch (mBehavior) {
case ParentThreadUnchangedBusyCount:
switch (mTarget) {
case ParentThread:
aWorkerPrivate->AssertIsOnParentThread();
break;
case WorkerThreadModifyBusyCount:
aWorkerPrivate->AssertIsOnWorkerThread();
break;
case WorkerThreadUnchangedBusyCount:
case WorkerThread:
aWorkerPrivate->AssertIsOnWorkerThread();
break;
@ -193,10 +171,6 @@ void WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
}
#endif
if (mBehavior == WorkerThreadModifyBusyCount) {
aWorkerPrivate->ModifyBusyCountFromWorker(false);
}
}
// static
@ -230,14 +204,13 @@ NS_INTERFACE_MAP_END
NS_IMETHODIMP
WorkerRunnable::Run() {
LOG(("WorkerRunnable::Run [%p]", this));
bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
mBehavior == WorkerThreadUnchangedBusyCount;
bool targetIsWorkerThread = mTarget == WorkerThread;
#ifdef DEBUG
if (targetIsWorkerThread) {
mWorkerPrivate->AssertIsOnWorkerThread();
} else {
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
MOZ_ASSERT(mTarget == ParentThread);
mWorkerPrivate->AssertIsOnParentThread();
}
#endif
@ -247,9 +220,6 @@ WorkerRunnable::Run() {
mCallingCancelWithinRun = true;
Cancel();
mCallingCancelWithinRun = false;
if (mBehavior == WorkerThreadModifyBusyCount) {
mWorkerPrivate->ModifyBusyCountFromWorker(false);
}
return NS_OK;
}
@ -409,7 +379,7 @@ void WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
nsIEventTarget* aSyncLoopTarget)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, WorkerThread),
mSyncLoopTarget(aSyncLoopTarget) {
#ifdef DEBUG
if (mSyncLoopTarget) {
@ -420,7 +390,7 @@ WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
WorkerSyncRunnable::WorkerSyncRunnable(
WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
: WorkerRunnable(aWorkerPrivate, WorkerThread),
mSyncLoopTarget(std::move(aSyncLoopTarget)) {
#ifdef DEBUG
if (mSyncLoopTarget) {
@ -491,12 +461,9 @@ void MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
#ifdef DEBUG
WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
: WorkerRunnable(aWorkerPrivate, aBehavior) {
Target aTarget)
: WorkerRunnable(aWorkerPrivate, aTarget) {
MOZ_ASSERT(aWorkerPrivate);
MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
aBehavior == WorkerThreadUnchangedBusyCount,
"WorkerControlRunnables should not modify the busy count");
}
#endif
@ -512,7 +479,7 @@ nsresult WorkerControlRunnable::Cancel() {
bool WorkerControlRunnable::DispatchInternal() {
RefPtr<WorkerControlRunnable> runnable(this);
if (mBehavior == WorkerThreadUnchangedBusyCount) {
if (mTarget == WorkerThread) {
return NS_SUCCEEDED(
mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
}
@ -591,26 +558,13 @@ WorkerMainThreadRunnable::Run() {
}
bool WorkerSameThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
// We don't call WorkerRunnable::PreDispatch, because we're using
// WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
// that PreDispatch is on the parent thread in that case.
aWorkerPrivate->AssertIsOnWorkerThread();
return true;
}
void WorkerSameThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) {
// We don't call WorkerRunnable::PostDispatch, because we're using
// WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
// that PostDispatch is on the parent thread in that case.
aWorkerPrivate->AssertIsOnWorkerThread();
if (aDispatchResult) {
DebugOnly<bool> willIncrement =
aWorkerPrivate->ModifyBusyCountFromWorker(true);
// Should never fail since if this thread is still running, so should the
// parent and it should be able to process a control runnable.
MOZ_ASSERT(willIncrement);
}
}
WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable()
@ -697,7 +651,7 @@ void WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread() {
void WorkerProxyToMainThreadRunnable::ReleaseWorker() { mWorkerRef = nullptr; }
bool WorkerDebuggeeRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
if (mBehavior == ParentThreadUnchangedBusyCount) {
if (mTarget == ParentThread) {
RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create(
aWorkerPrivate, "WorkerDebuggeeRunnable::mSender");
if (!strongRef) {

View File

@ -34,24 +34,15 @@ namespace dom {
class WorkerPrivate;
// Use this runnable to communicate from the worker to its parent or vice-versa.
// The busy count must be taken into consideration and declared at construction
// time.
class WorkerRunnable : public nsIRunnable {
public:
enum TargetAndBusyBehavior {
enum Target {
// Target the main thread for top-level workers, otherwise target the
// WorkerThread of the worker's parent. No change to the busy count.
ParentThreadUnchangedBusyCount,
// WorkerThread of the worker's parent.
ParentThread,
// Target the thread where the worker event loop runs. The busy count will
// be incremented before dispatching and decremented (asynchronously) after
// running.
WorkerThreadModifyBusyCount,
// Target the thread where the worker event loop runs. The busy count will
// not be modified in any way. Besides worker-internal runnables this is
// almost always the wrong choice.
WorkerThreadUnchangedBusyCount
// Target the thread where the worker event loop runs.
WorkerThread,
};
protected:
@ -59,7 +50,7 @@ class WorkerRunnable : public nsIRunnable {
WorkerPrivate* mWorkerPrivate;
// See above.
TargetAndBusyBehavior mBehavior;
Target mTarget;
private:
// Whether or not Cancel() is currently being called from inside the Run()
@ -90,13 +81,12 @@ class WorkerRunnable : public nsIRunnable {
static WorkerRunnable* FromRunnable(nsIRunnable* aRunnable);
protected:
WorkerRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior = WorkerThreadModifyBusyCount)
WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget = WorkerThread)
#ifdef DEBUG
;
#else
: mWorkerPrivate(aWorkerPrivate),
mBehavior(aBehavior),
mTarget(aTarget),
mCallingCancelWithinRun(false) {
}
#endif
@ -111,13 +101,11 @@ class WorkerRunnable : public nsIRunnable {
nsIGlobalObject* DefaultGlobalObject() const;
// By default asserts that Dispatch() is being called on the right thread
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
// Also increments the busy count of |mWorkerPrivate| if targeting the
// WorkerThread.
// (ParentThread if |mTarget| is WorkerThread).
virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate);
// By default asserts that Dispatch() is being called on the right thread
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
// (ParentThread if |mTarget| is WorkerThread).
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
bool aDispatchResult);
@ -133,11 +121,11 @@ class WorkerRunnable : public nsIRunnable {
// Must be implemented by subclasses. Called on the target thread. The return
// value will be passed to PostRun(). The JSContext passed in here comes from
// an AutoJSAPI (or AutoEntryScript) that we set up on the stack. If
// mBehavior is ParentThreadUnchangedBusyCount, it is in the compartment of
// mTarget is ParentThread, it is in the compartment of
// mWorkerPrivate's reflector (i.e. the worker object in the parent thread),
// unless that reflector is null, in which case it's in the compartment of the
// parent global (which is the compartment reflector would have been in), or
// in the null compartment if there is no parent global. For other mBehavior
// in the null compartment if there is no parent global. For other mTarget
// values, we're running on the worker thread and aCx is in whatever
// compartment GetCurrentWorkerThreadJSContext() was in when
// nsIRunnable::Run() got called. This is actually important for cases when a
@ -154,8 +142,7 @@ class WorkerRunnable : public nsIRunnable {
virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
// By default asserts that Run() (and WorkerRun()) were called on the correct
// thread. Also sends an asynchronous message to the ParentThread if the
// busy count was previously modified in PreDispatch().
// thread.
//
// The aCx passed here is the same one as was passed to WorkerRun and is
// still in the same compartment. PostRun implementations must NOT leave an
@ -175,7 +162,7 @@ class WorkerRunnable : public nsIRunnable {
class WorkerDebuggerRunnable : public WorkerRunnable {
protected:
explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerRunnable(aWorkerPrivate, WorkerThread) {}
virtual ~WorkerDebuggerRunnable() = default;
@ -244,18 +231,16 @@ class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable {
// This runnable is processed as soon as it is received by the worker,
// potentially running before previously queued runnables and perhaps even with
// other JS code executing on the stack. These runnables must not alter the
// state of the JS runtime and should only twiddle state values. The busy count
// is never modified.
// state of the JS runtime and should only twiddle state values.
class WorkerControlRunnable : public WorkerRunnable {
friend class WorkerPrivate;
protected:
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget)
#ifdef DEBUG
;
#else
: WorkerRunnable(aWorkerPrivate, aBehavior) {
: WorkerRunnable(aWorkerPrivate, aTarget) {
}
#endif
@ -278,7 +263,7 @@ class WorkerControlRunnable : public WorkerRunnable {
class MainThreadWorkerRunnable : public WorkerRunnable {
protected:
explicit MainThreadWorkerRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {
: WorkerRunnable(aWorkerPrivate, WorkerThread) {
AssertIsOnMainThread();
}
@ -300,7 +285,7 @@ class MainThreadWorkerRunnable : public WorkerRunnable {
class MainThreadWorkerControlRunnable : public WorkerControlRunnable {
protected:
explicit MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
: WorkerControlRunnable(aWorkerPrivate, WorkerThread) {}
virtual ~MainThreadWorkerControlRunnable() = default;
@ -316,15 +301,14 @@ class MainThreadWorkerControlRunnable : public WorkerControlRunnable {
};
// A WorkerRunnable that should be dispatched from the worker to itself for
// async tasks. This will increment the busy count PostDispatch() (only if
// dispatch was successful) and decrement it in PostRun().
// async tasks.
//
// Async tasks will almost always want to use this since
// a WorkerSameThreadRunnable keeps the Worker from being GCed.
class WorkerSameThreadRunnable : public WorkerRunnable {
protected:
explicit WorkerSameThreadRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) {}
: WorkerRunnable(aWorkerPrivate, WorkerThread) {}
virtual ~WorkerSameThreadRunnable() = default;
@ -404,9 +388,7 @@ class WorkerProxyToMainThreadRunnable : public Runnable {
};
// This runnable is used to stop a sync loop and it's meant to be used on the
// main-thread only. As sync loops keep the busy count incremented as long as
// they run this runnable does not modify the busy count
// in any way.
// main-thread only.
class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable {
nsresult mResult;
@ -460,10 +442,9 @@ class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable {
// a top-level worker also pauses mMainThreadDebuggeeEventTarget.
class WorkerDebuggeeRunnable : public WorkerRunnable {
protected:
WorkerDebuggeeRunnable(
WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior = ParentThreadUnchangedBusyCount)
: WorkerRunnable(aWorkerPrivate, aBehavior) {}
WorkerDebuggeeRunnable(WorkerPrivate* aWorkerPrivate,
Target aTarget = ParentThread)
: WorkerRunnable(aWorkerPrivate, aTarget) {}
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override;
@ -480,7 +461,7 @@ class WorkerDebuggeeRunnable : public WorkerRunnable {
// WorkerPrivate::GetWindow may only be used on the main thread.
//
// Runnables sent downwards, from content to a worker or from a worker to a
// child, keep the sender alive because they are WorkerThreadModifyBusyCount
// child, keep the sender alive because they are WorkerThread
// runnables, and so leave this null.
RefPtr<ThreadSafeWorkerRef> mSender;
};

View File

@ -290,6 +290,8 @@ class IProtocol : public HasResultCodes {
virtual void AllManagedActors(
nsTArray<RefPtr<ActorLifecycleProxy>>& aActors) const = 0;
virtual uint32_t AllManagedActorsCount() const = 0;
// Internal method called when the actor becomes connected.
void ActorConnected();

View File

@ -3973,6 +3973,38 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
self.cls.addstmts([managedmeth, Whitespace.NL])
# AllManagedActorsCount() const
managedmeth = MethodDefn(
MethodDecl(
"AllManagedActorsCount",
ret=Type.UINT32,
methodspec=MethodSpec.OVERRIDE,
const=True,
)
)
# Count the number of managed actors.
managedmeth.addcode(
"""
uint32_t total = 0;
"""
)
for managed in ptype.manages:
managedmeth.addcode(
"""
total += ${container}.Count();
""",
container=p.managedVar(managed, self.side),
)
managedmeth.addcode(
"""
return total;
"""
)
self.cls.addstmts([managedmeth, Whitespace.NL])
# OpenPEndpoint(...)/BindPEndpoint(...)
for managed in ptype.manages:
self.genManagedEndpoint(managed)

View File

@ -134,7 +134,7 @@ class ExtensionListenerCallWorkerRunnable : public dom::WorkerRunnable {
ListenerCallOptions* aCallOptions,
RefPtr<dom::Promise> aPromiseRetval = nullptr)
: WorkerRunnable(aExtensionEventListener->GetWorkerPrivate(),
WorkerThreadUnchangedBusyCount),
WorkerThread),
mListener(aExtensionEventListener),
mArgsHolder(std::move(aArgsHolder)),
mPromiseResult(std::move(aPromiseRetval)),