1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/* -*- 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 "ServiceWorkerShutdownState.h"
#include "mozilla/HashTable.h"
#include "mozilla/InitializedOnce.h"
#include "mozilla/MozPromise.h"
#include "mozilla/NotNull.h"
#include "nsCOMPtr.h"
#include "nsIAsyncShutdown.h"
#include "nsISupportsImpl.h"
#include "nsITimer.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__
|