summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/ServiceWorkerShutdownBlocker.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/serviceworkers/ServiceWorkerShutdownBlocker.h')
-rw-r--r--dom/serviceworkers/ServiceWorkerShutdownBlocker.h157
1 files changed, 157 insertions, 0 deletions
diff --git a/dom/serviceworkers/ServiceWorkerShutdownBlocker.h b/dom/serviceworkers/ServiceWorkerShutdownBlocker.h
new file mode 100644
index 0000000000..a200325c5b
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerShutdownBlocker.h
@@ -0,0 +1,157 @@
+/* -*- 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_serviceworkershutdownblocker_h__
+#define mozilla_dom_serviceworkershutdownblocker_h__
+
+#include "nsCOMPtr.h"
+#include "nsIAsyncShutdown.h"
+#include "nsISupportsImpl.h"
+#include "nsITimer.h"
+
+#include "ServiceWorkerShutdownState.h"
+#include "mozilla/InitializedOnce.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/HashTable.h"
+
+namespace mozilla::dom {
+
+class ServiceWorkerManager;
+
+/**
+ * Main thread only.
+ *
+ * A ServiceWorkerShutdownBlocker will "accept promises", and each of these
+ * promises will be a "pending promise" while it hasn't settled. At some point,
+ * `StopAcceptingPromises()` should be called and the state will change to "not
+ * accepting promises" (this is a one way state transition). The shutdown phase
+ * of the shutdown client the blocker is created with will be blocked until
+ * there are no more pending promises.
+ *
+ * It doesn't matter whether the state changes to "not accepting promises"
+ * before or during the associated shutdown phase.
+ *
+ * In beta/release builds there will be an additional timer that starts ticking
+ * once both the shutdown phase has been reached and the state is "not accepting
+ * promises". If when the timer expire there are still pending promises,
+ * shutdown will be forcefully unblocked.
+ */
+class ServiceWorkerShutdownBlocker final : public nsIAsyncShutdownBlocker,
+ public nsITimerCallback,
+ public nsINamed {
+ public:
+ using Progress = ServiceWorkerShutdownState::Progress;
+ static const uint32_t kInvalidShutdownStateId = 0;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIASYNCSHUTDOWNBLOCKER
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ /**
+ * Returns the registered shutdown blocker if registration succeeded and
+ * nullptr otherwise.
+ */
+ static already_AddRefed<ServiceWorkerShutdownBlocker> CreateAndRegisterOn(
+ nsIAsyncShutdownClient& aShutdownBarrier,
+ ServiceWorkerManager& aServiceWorkerManager);
+
+ /**
+ * Blocks shutdown until `aPromise` settles.
+ *
+ * Can be called multiple times, and shutdown will be blocked until all the
+ * calls' promises settle, but all of these calls must happen before
+ * `StopAcceptingPromises()` is called (assertions will enforce this).
+ *
+ * See `CreateShutdownState` for aShutdownStateId, which is needed to clear
+ * the shutdown state if the shutdown process aborts for some reason.
+ */
+ void WaitOnPromise(GenericNonExclusivePromise* aPromise,
+ uint32_t aShutdownStateId);
+
+ /**
+ * Once this is called, shutdown will be blocked until all promises
+ * passed to `WaitOnPromise()` settle, and there must be no more calls to
+ * `WaitOnPromise()` (assertions will enforce this).
+ */
+ void StopAcceptingPromises();
+
+ /**
+ * Start tracking the shutdown of an individual ServiceWorker for hang
+ * reporting purposes. Returns a "shutdown state ID" that should be used
+ * in subsequent calls to ReportShutdownProgress. The shutdown of an
+ * individual ServiceWorker is presumed to be completed when its `Progress`
+ * reaches `Progress::ShutdownCompleted`.
+ */
+ uint32_t CreateShutdownState();
+
+ void ReportShutdownProgress(uint32_t aShutdownStateId, Progress aProgress);
+
+ private:
+ explicit ServiceWorkerShutdownBlocker(
+ ServiceWorkerManager& aServiceWorkerManager);
+
+ ~ServiceWorkerShutdownBlocker();
+
+ /**
+ * No-op if any of the following are true:
+ * 1) `BlockShutdown()` hasn't been called yet, or
+ * 2) `StopAcceptingPromises()` hasn't been called yet, or
+ * 3) `StopAcceptingPromises()` HAS been called, but there are still pending
+ * promises.
+ */
+ void MaybeUnblockShutdown();
+
+ /**
+ * Requires `BlockShutdown()` to have been called.
+ */
+ void UnblockShutdown();
+
+ /**
+ * Returns the remaining pending promise count (i.e. excluding the promise
+ * that just settled).
+ */
+ uint32_t PromiseSettled();
+
+ bool IsAcceptingPromises() const;
+
+ uint32_t GetPendingPromises() const;
+
+ /**
+ * Initializes a timer that will unblock shutdown unconditionally once it's
+ * expired (even if there are still pending promises). No-op if:
+ * 1) not a beta or release build, or
+ * 2) shutdown is not being blocked or `StopAcceptingPromises()` has not been
+ * called.
+ */
+ void MaybeInitUnblockShutdownTimer();
+
+ struct AcceptingPromises {
+ uint32_t mPendingPromises = 0;
+ };
+
+ struct NotAcceptingPromises {
+ explicit NotAcceptingPromises(AcceptingPromises aPreviousState);
+
+ uint32_t mPendingPromises = 0;
+ };
+
+ Variant<AcceptingPromises, NotAcceptingPromises> mState;
+
+ nsCOMPtr<nsIAsyncShutdownClient> mShutdownClient;
+
+ HashMap<uint32_t, ServiceWorkerShutdownState> mShutdownStates;
+
+ nsCOMPtr<nsITimer> mTimer;
+ LazyInitializedOnceEarlyDestructible<
+ const NotNull<RefPtr<ServiceWorkerManager>>>
+ mServiceWorkerManager;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_serviceworkershutdownblocker_h__