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:
Perry Jiang 2019-08-14 16:19:44 +00:00
Родитель 7e18d0584f
Коммит 69fbf004ee
3 изменённых файлов: 267 добавлений и 0 удалений

Просмотреть файл

@ -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',