зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1231213 - Implement ServiceWorkerShutdownBlocker. r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D26165 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f5b150b31b
Коммит
1e8e727d16
|
@ -0,0 +1,174 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "ServiceWorkerShutdownBlocker.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerShutdownBlocker, nsIAsyncShutdownBlocker)
|
||||
|
||||
NS_IMETHODIMP ServiceWorkerShutdownBlocker::GetName(nsAString& aNameOut) {
|
||||
aNameOut = NS_LITERAL_STRING(
|
||||
"ServiceWorkerShutdownBlocker: shutting down Service Workers");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aClient) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mShutdownClient);
|
||||
|
||||
mShutdownClient = aClient;
|
||||
MaybeUnblockShutdown();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP ServiceWorkerShutdownBlocker::GetState(nsIPropertyBag** aBagOut) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aBagOut);
|
||||
|
||||
nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
|
||||
do_CreateInstance("@mozilla.org/hash-property-bag;1");
|
||||
|
||||
if (NS_WARN_IF(!propertyBag)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsresult rv = propertyBag->SetPropertyAsBool(
|
||||
NS_LITERAL_STRING("acceptingPromises"), IsAcceptingPromises());
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = propertyBag->SetPropertyAsUint32(NS_LITERAL_STRING("pendingPromises"),
|
||||
GetPendingPromises());
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
propertyBag.forget(aBagOut);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ServiceWorkerShutdownBlocker>
|
||||
ServiceWorkerShutdownBlocker::CreateAndRegisterOn(
|
||||
nsIAsyncShutdownClient* aShutdownBarrier) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aShutdownBarrier);
|
||||
|
||||
RefPtr<ServiceWorkerShutdownBlocker> blocker =
|
||||
new ServiceWorkerShutdownBlocker();
|
||||
|
||||
nsresult rv = aShutdownBarrier->AddBlocker(
|
||||
blocker.get(), NS_LITERAL_STRING(__FILE__), __LINE__,
|
||||
NS_LITERAL_STRING("Service Workers shutdown"));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return blocker.forget();
|
||||
}
|
||||
|
||||
void ServiceWorkerShutdownBlocker::WaitOnPromise(
|
||||
GenericNonExclusivePromise* aPromise) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(IsAcceptingPromises());
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
++mState.as<AcceptingPromises>().mPendingPromises;
|
||||
|
||||
RefPtr<ServiceWorkerShutdownBlocker> self = this;
|
||||
|
||||
aPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[self = std::move(self)](
|
||||
const GenericNonExclusivePromise::ResolveOrRejectValue&) {
|
||||
if (!self->PromiseSettled()) {
|
||||
self->MaybeUnblockShutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServiceWorkerShutdownBlocker::StopAcceptingPromises() {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(IsAcceptingPromises());
|
||||
|
||||
mState = AsVariant(NotAcceptingPromises(mState.as<AcceptingPromises>()));
|
||||
}
|
||||
|
||||
ServiceWorkerShutdownBlocker::ServiceWorkerShutdownBlocker()
|
||||
: mState(VariantType<AcceptingPromises>()) {
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
ServiceWorkerShutdownBlocker::~ServiceWorkerShutdownBlocker() {
|
||||
MOZ_ASSERT(!IsAcceptingPromises());
|
||||
MOZ_ASSERT(!GetPendingPromises());
|
||||
MOZ_ASSERT(!mShutdownClient);
|
||||
}
|
||||
|
||||
void ServiceWorkerShutdownBlocker::MaybeUnblockShutdown() {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mShutdownClient || IsAcceptingPromises() || GetPendingPromises()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mShutdownClient->RemoveBlocker(this);
|
||||
mShutdownClient = nullptr;
|
||||
}
|
||||
|
||||
uint32_t ServiceWorkerShutdownBlocker::PromiseSettled() {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(GetPendingPromises());
|
||||
|
||||
if (IsAcceptingPromises()) {
|
||||
return --mState.as<AcceptingPromises>().mPendingPromises;
|
||||
}
|
||||
|
||||
return --mState.as<NotAcceptingPromises>().mPendingPromises;
|
||||
}
|
||||
|
||||
bool ServiceWorkerShutdownBlocker::IsAcceptingPromises() const {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return mState.is<AcceptingPromises>();
|
||||
}
|
||||
|
||||
uint32_t ServiceWorkerShutdownBlocker::GetPendingPromises() const {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (IsAcceptingPromises()) {
|
||||
return mState.as<AcceptingPromises>().mPendingPromises;
|
||||
}
|
||||
|
||||
return mState.as<NotAcceptingPromises>().mPendingPromises;
|
||||
}
|
||||
|
||||
ServiceWorkerShutdownBlocker::NotAcceptingPromises::NotAcceptingPromises(
|
||||
AcceptingPromises aPreviousState)
|
||||
: mPendingPromises(aPreviousState.mPendingPromises) {
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- 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 "mozilla/MozPromise.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* Main thread only.
|
||||
*/
|
||||
class ServiceWorkerShutdownBlocker final : public nsIAsyncShutdownBlocker {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
|
||||
|
||||
/**
|
||||
* Returns the registered shutdown blocker if registration succeeded and
|
||||
* nullptr otherwise.
|
||||
*/
|
||||
static already_AddRefed<ServiceWorkerShutdownBlocker> CreateAndRegisterOn(
|
||||
nsIAsyncShutdownClient* aShutdownBarrier);
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
void WaitOnPromise(GenericNonExclusivePromise* aPromise);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
private:
|
||||
ServiceWorkerShutdownBlocker();
|
||||
|
||||
~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();
|
||||
|
||||
/**
|
||||
* Returns the remaining pending promise count (i.e. excluding the promise
|
||||
* that just settled).
|
||||
*/
|
||||
uint32_t PromiseSettled();
|
||||
|
||||
bool IsAcceptingPromises() const;
|
||||
|
||||
uint32_t GetPendingPromises() const;
|
||||
|
||||
struct AcceptingPromises {
|
||||
uint32_t mPendingPromises = 0;
|
||||
};
|
||||
|
||||
struct NotAcceptingPromises {
|
||||
explicit NotAcceptingPromises(AcceptingPromises aPreviousState);
|
||||
|
||||
uint32_t mPendingPromises = 0;
|
||||
};
|
||||
|
||||
Variant<AcceptingPromises, NotAcceptingPromises> mState;
|
||||
|
||||
nsCOMPtr<nsIAsyncShutdownClient> mShutdownClient;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_serviceworkershutdownblocker_h__
|
|
@ -65,6 +65,7 @@ UNIFIED_SOURCES += [
|
|||
'ServiceWorkerRegistrationParent.cpp',
|
||||
'ServiceWorkerRegistrationProxy.cpp',
|
||||
'ServiceWorkerScriptCache.cpp',
|
||||
'ServiceWorkerShutdownBlocker.cpp',
|
||||
'ServiceWorkerUnregisterCallback.cpp',
|
||||
'ServiceWorkerUnregisterJob.cpp',
|
||||
'ServiceWorkerUpdateJob.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче