gecko-dev/dom/workers/MessageEventRunnable.cpp
Eden Chuang ea1bf2603a Bug 1800659 - P3 Remove WorkerPrivate::ClearMainEventQueue() r=asuth,smaug
Pending->Canceling->Killing (WorkerNeverRan)

Need to clear WorkerPrivate::mPreStartRunnables

Could we call WorkerRunnable::WorkerRun() to release resource? There could be no WorkerGlobalScope...

Pending->Running->Closing->Canceling->Killing(WorkerRan)
Pending->Running->Canceling->Killing(WorkerRan)

When entering “Closing”
1. Keeping receives normal WorkerRunnables
2. Making ParentStatus as Closing
3. Cancel all timeout
4. Don’t clear the main event queue anymore. But we still wait for all SyncLoops be completed.
5. Call WorkerGlobalScope::NoteTerminating() and nsIGlobalObject::DisconnectEventTargetObjects().
6. Switching to “Canceling” by asking parent thread to call WorkerPrivate::Cancel()

When entering “Canceling”
1. Call WorkerGlobalScope::NoteTerminating()
2. Notify StrongWorkerRefs, worker is in “Canceling”, send and complete the corresponding shutdown work right now.
3. Executing all runnables until no normal WorkerRunnables in queue and no WorkerRefs, SyncLoops and children workers
4. Stop receiving normal WorkerRunnables and DisconnectEventTargetObjects of WorkerScope.
4. Entering “Killing”

When entering “Killing”
1. We would not notify WorkerRefs anymore. Logically all WorkerRefs should be released in “Canceling”
2. Executing all remaining ControlRunnables
3. Release corresponding resources of Worker on worker thread.

Depends on D173850

Differential Revision: https://phabricator.services.mozilla.com/D177511
2023-06-06 06:36:50 +00:00

164 lines
5.6 KiB
C++

/* -*- 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/. */
#include "MessageEventRunnable.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsQueryObject.h"
#include "WorkerScope.h"
namespace mozilla::dom {
MessageEventRunnable::MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior)
: WorkerDebuggeeRunnable(aWorkerPrivate, aBehavior),
StructuredCloneHolder(CloningSupported, TransferringSupported,
StructuredCloneScope::SameProcess) {}
bool MessageEventRunnable::DispatchDOMEvent(JSContext* aCx,
WorkerPrivate* aWorkerPrivate,
DOMEventTargetHelper* aTarget,
bool aIsMainThread) {
nsCOMPtr<nsIGlobalObject> parent = aTarget->GetParentObject();
// For some workers without window, parent is null and we try to find it
// from the JS Context.
if (!parent) {
JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx));
if (NS_WARN_IF(!globalObject)) {
return false;
}
parent = xpc::NativeGlobal(globalObject);
if (NS_WARN_IF(!parent)) {
return false;
}
}
MOZ_ASSERT(parent);
JS::Rooted<JS::Value> messageData(aCx);
IgnoredErrorResult rv;
UniquePtr<AbstractTimelineMarker> start;
UniquePtr<AbstractTimelineMarker> end;
bool isTimelineRecording = !TimelineConsumers::IsEmpty();
if (isTimelineRecording) {
start = MakeUnique<WorkerTimelineMarker>(
aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::START);
}
JS::CloneDataPolicy cloneDataPolicy;
if (parent->GetClientInfo().isSome() &&
parent->GetClientInfo()->AgentClusterId().isSome() &&
parent->GetClientInfo()->AgentClusterId()->Equals(
aWorkerPrivate->AgentClusterId())) {
cloneDataPolicy.allowIntraClusterClonableSharedObjects();
}
if (aWorkerPrivate->IsSharedMemoryAllowed()) {
cloneDataPolicy.allowSharedMemoryObjects();
}
Read(parent, aCx, &messageData, cloneDataPolicy, rv);
if (isTimelineRecording) {
end = MakeUnique<WorkerTimelineMarker>(
aIsMainThread
? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
: ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
MarkerTracingType::END);
TimelineConsumers::AddMarkerForAllObservedDocShells(start);
TimelineConsumers::AddMarkerForAllObservedDocShells(end);
}
if (NS_WARN_IF(rv.Failed())) {
DispatchError(aCx, aTarget);
return false;
}
Sequence<OwningNonNull<MessagePort>> ports;
if (!TakeTransferredPortsAsSequence(ports)) {
DispatchError(aCx, aTarget);
return false;
}
RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
Cancelable::eNo, messageData, u""_ns, u""_ns, nullptr,
ports);
event->SetTrusted(true);
aTarget->DispatchEvent(*event);
return true;
}
bool MessageEventRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) {
if (mBehavior == ParentThreadUnchangedBusyCount) {
// Don't fire this event if the JS object has been disconnected from the
// private object.
if (!aWorkerPrivate->IsAcceptingEvents()) {
return true;
}
// Once a window has frozen its workers, their
// mMainThreadDebuggeeEventTargets should be paused, and their
// WorkerDebuggeeRunnables should not be being executed. The same goes for
// WorkerDebuggeeRunnables sent from child to parent workers, but since a
// frozen parent worker runs only control runnables anyway, that is taken
// care of naturally.
MOZ_ASSERT(!aWorkerPrivate->IsFrozen());
// Similarly for paused windows; all its workers should have been informed.
// (Subworkers are unaffected by paused windows.)
MOZ_ASSERT(!aWorkerPrivate->IsParentWindowPaused());
aWorkerPrivate->AssertInnerWindowIsCorrect();
return DispatchDOMEvent(aCx, aWorkerPrivate,
aWorkerPrivate->ParentEventTargetRef(),
!aWorkerPrivate->GetParent());
}
MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
MOZ_ASSERT(aWorkerPrivate->GlobalScope());
// If the worker start shutting down, don't dispatch the message event.
if (NS_FAILED(
aWorkerPrivate->GlobalScope()->CheckCurrentGlobalCorrectness())) {
return true;
}
return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
false);
}
void MessageEventRunnable::DispatchError(JSContext* aCx,
DOMEventTargetHelper* aTarget) {
RootedDictionary<MessageEventInit> init(aCx);
init.mBubbles = false;
init.mCancelable = false;
RefPtr<Event> event =
MessageEvent::Constructor(aTarget, u"messageerror"_ns, init);
event->SetTrusted(true);
aTarget->DispatchEvent(*event);
}
} // namespace mozilla::dom