summaryrefslogtreecommitdiffstats
path: root/dom/workers/remoteworkers/RemoteWorkerController.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/workers/remoteworkers/RemoteWorkerController.h
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/workers/remoteworkers/RemoteWorkerController.h')
-rw-r--r--dom/workers/remoteworkers/RemoteWorkerController.h323
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