diff options
Diffstat (limited to 'dom/workers/remoteworkers/RemoteWorkerController.h')
-rw-r--r-- | dom/workers/remoteworkers/RemoteWorkerController.h | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/dom/workers/remoteworkers/RemoteWorkerController.h b/dom/workers/remoteworkers/RemoteWorkerController.h new file mode 100644 index 0000000000..af53634abd --- /dev/null +++ b/dom/workers/remoteworkers/RemoteWorkerController.h @@ -0,0 +1,323 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_RemoteWorkerController_h +#define mozilla_dom_RemoteWorkerController_h + +#include "nsISupportsImpl.h" +#include "nsTArray.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/DOMTypes.h" +#include "mozilla/dom/ServiceWorkerOpArgs.h" +#include "mozilla/dom/ServiceWorkerOpPromise.h" + +namespace mozilla::dom { + +/* Here's a graph about this remote workers are spawned. + * + * _________________________________ | ________________________________ + * | | | | | + * | Parent process | IPC | Creation of Process X | + * | PBackground thread | | | | + * | | | | [RemoteWorkerService::Init()] | + * | | | | | | + * | | | | | (1) | + * | [RemoteWorkerManager:: (2) | | | V | + * | RegisterActor()]<-------- [new RemoteWorkerServiceChild] | + * | | | | | + * | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | |________________________________| + * | | | + * | new SharedWorker/ServiceWorker | | + * | | ^ | IPC + * | (3) | (4)| | + * | V | | | + * | [RemoteWorkerController:: | | + * | | Create(data)] | | + * | | (5) | | + * | V | | + * | [RemoteWorkerManager::Launch()] | | + * | | | IPC _____________________________ + * | | (6) | | | | + * | | | | Selected content process | + * | V | (7) | | + * | [SendPRemoteWorkerConstructor()]--------->[new RemoteWorkerChild()] | + * | | | | | | | + * | | (8) | | | | | + * | V | | | V | + * | [RemoteWorkerController-> | | | RemoteWorkerChild->Exec() | + * | | SetControllerActor()] | | |_____________________________| + * | (9) | | IPC + * | V | | + * | [RemoteWorkerObserver-> | | + * | CreationCompleted()] | | + * |_________________________________| | + * | + * + * 1. When a new process starts, it creates a RemoteWorkerService singleton. + * This service creates a new thread (Worker Launcher) and from there, it + * starts a PBackground RemoteWorkerServiceChild actor. + * 2. On the parent process, PBackground thread, RemoteWorkerServiceParent + * actors are registered into the RemoteWorkerManager service. + * + * 3. At some point, a SharedWorker or a ServiceWorker must be executed. + * RemoteWorkerController::Create() is used to start the launching. This + * method must be called on the parent process, on the PBackground thread. + * 4. RemoteWorkerController object is immediately returned to the caller. Any + * operation done with this controller object will be stored in a queue, + * until the launching is correctly executed. + * 5. RemoteWorkerManager has the list of active RemoteWorkerServiceParent + * actors. From them, it picks one. + * In case we don't have any content process to select, a new one is + * spawned. If this happens, the operation is suspended until a new + * RemoteWorkerServiceParent is registered. + * 6. RemoteWorkerServiceParent is used to create a RemoteWorkerParent. + * 7. RemoteWorkerChild is created on a selected process and it executes the + * WorkerPrivate. + * 8. The RemoteWorkerParent actor is passed to the RemoteWorkerController. + * 9. RemoteWorkerController now is ready to continue and it called + * RemoteWorkerObserver to inform that the operation is completed. + * In case there were pending operations, they are now executed. + */ + +class ErrorValue; +class FetchEventOpParent; +class RemoteWorkerControllerParent; +class RemoteWorkerData; +class RemoteWorkerManager; +class RemoteWorkerParent; + +class RemoteWorkerObserver { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual void CreationFailed() = 0; + + virtual void CreationSucceeded() = 0; + + virtual void ErrorReceived(const ErrorValue& aValue) = 0; + + virtual void LockNotified(bool aCreated) = 0; + + virtual void WebTransportNotified(bool aCreated) = 0; + + virtual void Terminated() = 0; +}; + +/** + * PBackground instance created by static RemoteWorkerController::Create that + * builds on RemoteWorkerManager. Interface to control the remote worker as well + * as receive events via the RemoteWorkerObserver interface that the owner + * (SharedWorkerManager in this case) must implement to hear about errors, + * termination, and whether the initial spawning succeeded/failed. + * + * Its methods may be called immediately after creation even though the worker + * is created asynchronously; an internal operation queue makes this work. + * Communicates with the remote worker via owned RemoteWorkerParent over + * PRemoteWorker protocol. + */ +class RemoteWorkerController final { + friend class RemoteWorkerControllerParent; + friend class RemoteWorkerManager; + friend class RemoteWorkerParent; + + public: + NS_INLINE_DECL_REFCOUNTING(RemoteWorkerController) + + static already_AddRefed<RemoteWorkerController> Create( + const RemoteWorkerData& aData, RemoteWorkerObserver* aObserver, + base::ProcessId = 0); + + void AddWindowID(uint64_t aWindowID); + + void RemoveWindowID(uint64_t aWindowID); + + void AddPortIdentifier(const MessagePortIdentifier& aPortIdentifier); + + void Terminate(); + + void Suspend(); + + void Resume(); + + void Freeze(); + + void Thaw(); + + RefPtr<ServiceWorkerOpPromise> ExecServiceWorkerOp( + ServiceWorkerOpArgs&& aArgs); + + RefPtr<ServiceWorkerFetchEventOpPromise> ExecServiceWorkerFetchEventOp( + const ParentToParentServiceWorkerFetchEventOpArgs& aArgs, + RefPtr<FetchEventOpParent> aReal); + + RefPtr<GenericPromise> SetServiceWorkerSkipWaitingFlag() const; + + bool IsTerminated() const; + + void NotifyWebTransport(bool aCreated); + + private: + RemoteWorkerController(const RemoteWorkerData& aData, + RemoteWorkerObserver* aObserver); + + ~RemoteWorkerController(); + + void SetWorkerActor(RemoteWorkerParent* aActor); + + void NoteDeadWorkerActor(); + + void ErrorPropagation(const ErrorValue& aValue); + + void NotifyLock(bool aCreated); + + void WorkerTerminated(); + + void Shutdown(); + + void CreationFailed(); + + void CreationSucceeded(); + + void CancelAllPendingOps(); + + template <typename... Args> + void MaybeStartSharedWorkerOp(Args&&... aArgs); + + void NoteDeadWorker(); + + RefPtr<RemoteWorkerObserver> mObserver; + RefPtr<RemoteWorkerParent> mActor; + + enum { + ePending, + eReady, + eTerminated, + } mState; + + const bool mIsServiceWorker; + + /** + * `PendingOp` is responsible for encapsulating logic for starting and + * canceling pending remote worker operations, as this logic may vary + * depending on the type of the remote worker and the type of the operation. + */ + class PendingOp { + public: + PendingOp() = default; + + PendingOp(const PendingOp&) = delete; + + PendingOp& operator=(const PendingOp&) = delete; + + virtual ~PendingOp() = default; + + /** + * Returns `true` if execution has started or the operation is moot and + * doesn't need to be queued, `false` if execution hasn't started and the + * operation should be queued. In general, operations should only return + * false when a remote worker is first starting up. Operations may also + * somewhat non-intuitively return true without doing anything if the worker + * has already been told to shutdown. + * + * Starting execution may depend the state of `aOwner.` + */ + virtual bool MaybeStart(RemoteWorkerController* const aOwner) = 0; + + /** + * Invoked if the operation will never have MaybeStart() called again + * because the RemoteWorkerController has terminated (or will never start). + * This should be used by PendingOps to clean up any resources they own and + * may also be called internally by their MaybeStart() methods if they + * determine the worker has been terminated. This should be idempotent. + */ + virtual void Cancel() = 0; + }; + + class PendingSharedWorkerOp final : public PendingOp { + public: + enum Type { + eTerminate, + eSuspend, + eResume, + eFreeze, + eThaw, + ePortIdentifier, + eAddWindowID, + eRemoveWindowID, + }; + + explicit PendingSharedWorkerOp(Type aType, uint64_t aWindowID = 0); + + explicit PendingSharedWorkerOp( + const MessagePortIdentifier& aPortIdentifier); + + ~PendingSharedWorkerOp(); + + bool MaybeStart(RemoteWorkerController* const aOwner) override; + + void Cancel() override; + + private: + const Type mType; + const MessagePortIdentifier mPortIdentifier; + const uint64_t mWindowID = 0; + bool mCompleted = false; + }; + + class PendingServiceWorkerOp final : public PendingOp { + public: + PendingServiceWorkerOp(ServiceWorkerOpArgs&& aArgs, + RefPtr<ServiceWorkerOpPromise::Private> aPromise); + + ~PendingServiceWorkerOp(); + + bool MaybeStart(RemoteWorkerController* const aOwner) override; + + void Cancel() override; + + private: + ServiceWorkerOpArgs mArgs; + RefPtr<ServiceWorkerOpPromise::Private> mPromise; + }; + + /** + * Custom pending op type to deal with the complexities of FetchEvents having + * their own actor. + * + * FetchEvent Ops have their own actor type because their lifecycle is more + * complex than IPDL's async return value mechanism allows. Additionally, + * its IPC struct potentially has to serialize RemoteLazyStreams which + * requires us to hold an nsIInputStream when at rest and serialize it when + * eventually sending. + */ + class PendingSWFetchEventOp final : public PendingOp { + public: + PendingSWFetchEventOp( + const ParentToParentServiceWorkerFetchEventOpArgs& aArgs, + RefPtr<ServiceWorkerFetchEventOpPromise::Private> aPromise, + RefPtr<FetchEventOpParent>&& aReal); + + ~PendingSWFetchEventOp(); + + bool MaybeStart(RemoteWorkerController* const aOwner) override; + + void Cancel() override; + + private: + ParentToParentServiceWorkerFetchEventOpArgs mArgs; + RefPtr<ServiceWorkerFetchEventOpPromise::Private> mPromise; + RefPtr<FetchEventOpParent> mReal; + nsCOMPtr<nsIInputStream> mBodyStream; + }; + + nsTArray<UniquePtr<PendingOp>> mPendingOps; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_RemoteWorkerController_h |