ServiceWorkerShutdownBlocker.h (5328B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_serviceworkershutdownblocker_h__ 8 #define mozilla_dom_serviceworkershutdownblocker_h__ 9 10 #include "ServiceWorkerShutdownState.h" 11 #include "mozilla/HashTable.h" 12 #include "mozilla/InitializedOnce.h" 13 #include "mozilla/MozPromise.h" 14 #include "mozilla/NotNull.h" 15 #include "nsCOMPtr.h" 16 #include "nsIAsyncShutdown.h" 17 #include "nsISupportsImpl.h" 18 #include "nsITimer.h" 19 20 namespace mozilla::dom { 21 22 class ServiceWorkerManager; 23 24 /** 25 * Main thread only. 26 * 27 * A ServiceWorkerShutdownBlocker will "accept promises", and each of these 28 * promises will be a "pending promise" while it hasn't settled. At some point, 29 * `StopAcceptingPromises()` should be called and the state will change to "not 30 * accepting promises" (this is a one way state transition). The shutdown phase 31 * of the shutdown client the blocker is created with will be blocked until 32 * there are no more pending promises. 33 * 34 * It doesn't matter whether the state changes to "not accepting promises" 35 * before or during the associated shutdown phase. 36 * 37 * In beta/release builds there will be an additional timer that starts ticking 38 * once both the shutdown phase has been reached and the state is "not accepting 39 * promises". If when the timer expire there are still pending promises, 40 * shutdown will be forcefully unblocked. 41 */ 42 class ServiceWorkerShutdownBlocker final : public nsIAsyncShutdownBlocker, 43 public nsITimerCallback, 44 public nsINamed { 45 public: 46 using Progress = ServiceWorkerShutdownState::Progress; 47 static const uint32_t kInvalidShutdownStateId = 0; 48 49 NS_DECL_ISUPPORTS 50 NS_DECL_NSIASYNCSHUTDOWNBLOCKER 51 NS_DECL_NSITIMERCALLBACK 52 NS_DECL_NSINAMED 53 54 /** 55 * Returns the registered shutdown blocker if registration succeeded and 56 * nullptr otherwise. 57 */ 58 static already_AddRefed<ServiceWorkerShutdownBlocker> CreateAndRegisterOn( 59 nsIAsyncShutdownClient& aShutdownBarrier, 60 ServiceWorkerManager& aServiceWorkerManager); 61 62 /** 63 * Blocks shutdown until `aPromise` settles. 64 * 65 * Can be called multiple times, and shutdown will be blocked until all the 66 * calls' promises settle, but all of these calls must happen before 67 * `StopAcceptingPromises()` is called (assertions will enforce this). 68 * 69 * See `CreateShutdownState` for aShutdownStateId, which is needed to clear 70 * the shutdown state if the shutdown process aborts for some reason. 71 */ 72 void WaitOnPromise(GenericNonExclusivePromise* aPromise, 73 uint32_t aShutdownStateId); 74 75 /** 76 * Once this is called, shutdown will be blocked until all promises 77 * passed to `WaitOnPromise()` settle, and there must be no more calls to 78 * `WaitOnPromise()` (assertions will enforce this). 79 */ 80 void StopAcceptingPromises(); 81 82 /** 83 * Start tracking the shutdown of an individual ServiceWorker for hang 84 * reporting purposes. Returns a "shutdown state ID" that should be used 85 * in subsequent calls to ReportShutdownProgress. The shutdown of an 86 * individual ServiceWorker is presumed to be completed when its `Progress` 87 * reaches `Progress::ShutdownCompleted`. 88 */ 89 uint32_t CreateShutdownState(); 90 91 void ReportShutdownProgress(uint32_t aShutdownStateId, Progress aProgress); 92 93 private: 94 explicit ServiceWorkerShutdownBlocker( 95 ServiceWorkerManager& aServiceWorkerManager); 96 97 ~ServiceWorkerShutdownBlocker(); 98 99 /** 100 * No-op if any of the following are true: 101 * 1) `BlockShutdown()` hasn't been called yet, or 102 * 2) `StopAcceptingPromises()` hasn't been called yet, or 103 * 3) `StopAcceptingPromises()` HAS been called, but there are still pending 104 * promises. 105 */ 106 void MaybeUnblockShutdown(); 107 108 /** 109 * Requires `BlockShutdown()` to have been called. 110 */ 111 void UnblockShutdown(); 112 113 /** 114 * Returns the remaining pending promise count (i.e. excluding the promise 115 * that just settled). 116 */ 117 uint32_t PromiseSettled(); 118 119 bool IsAcceptingPromises() const; 120 121 uint32_t GetPendingPromises() const; 122 123 /** 124 * Initializes a timer that will unblock shutdown unconditionally once it's 125 * expired (even if there are still pending promises). No-op if: 126 * 1) not a beta or release build, or 127 * 2) shutdown is not being blocked or `StopAcceptingPromises()` has not been 128 * called. 129 */ 130 void MaybeInitUnblockShutdownTimer(); 131 132 struct AcceptingPromises { 133 uint32_t mPendingPromises = 0; 134 }; 135 136 struct NotAcceptingPromises { 137 explicit NotAcceptingPromises(AcceptingPromises aPreviousState); 138 139 uint32_t mPendingPromises = 0; 140 }; 141 142 Variant<AcceptingPromises, NotAcceptingPromises> mState; 143 144 nsCOMPtr<nsIAsyncShutdownClient> mShutdownClient; 145 146 HashMap<uint32_t, ServiceWorkerShutdownState> mShutdownStates; 147 148 nsCOMPtr<nsITimer> mTimer; 149 LazyInitializedOnceEarlyDestructible< 150 const NotNull<RefPtr<ServiceWorkerManager>>> 151 mServiceWorkerManager; 152 }; 153 154 } // namespace mozilla::dom 155 156 #endif // mozilla_dom_serviceworkershutdownblocker_h__