Bug 1352401 - Part 2: Count active IDBOpenRequest, IndexedDB databases/transactions. r=janv

--HG--
extra : rebase_source : 87263d63be9021fe2493beb4b874c7e2749fd665
This commit is contained in:
Bevis Tseng 2017-06-16 19:06:36 +08:00
parent da26e6fa39
commit d6b1d1a397
9 changed files with 242 additions and 57 deletions

View File

@ -2227,17 +2227,7 @@ BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
request,
aNextObjectStoreId,
aNextIndexId);
if (NS_WARN_IF(!transaction)) {
// This can happen if we receive events after a worker has begun its
// shutdown process.
MOZ_ASSERT(!NS_IsMainThread());
// Report this to the console.
IDB_REPORT_INTERNAL_ERR();
MOZ_ALWAYS_TRUE(aActor->SendDeleteMe());
return IPC_OK();
}
MOZ_ASSERT(transaction);
transaction->AssertIsOnOwningThread();

View File

@ -177,6 +177,7 @@ IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
, mClosed(false)
, mInvalidated(false)
, mQuotaExceeded(false)
, mIncreasedActiveDatabaseCount(false)
{
MOZ_ASSERT(aRequest);
MOZ_ASSERT(aFactory);
@ -189,6 +190,7 @@ IDBDatabase::~IDBDatabase()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mBackgroundActor);
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
}
// static
@ -219,11 +221,8 @@ IDBDatabase::Create(IDBOpenDBRequest* aRequest,
MOZ_ASSERT(obsSvc);
// This topic must be successfully registered.
if (NS_WARN_IF(NS_FAILED(
obsSvc->AddObserver(observer, kWindowObserverTopic, false)))) {
observer->Revoke();
return nullptr;
}
MOZ_ALWAYS_SUCCEEDS(
obsSvc->AddObserver(observer, kWindowObserverTopic, false));
// These topics are not crucial.
if (NS_FAILED(obsSvc->AddObserver(observer,
@ -239,6 +238,8 @@ IDBDatabase::Create(IDBOpenDBRequest* aRequest,
}
}
db->IncreaseActiveDatabaseCount();
return db.forget();
}
@ -289,6 +290,10 @@ IDBDatabase::CloseInternal()
if (mBackgroundActor && !mInvalidated) {
mBackgroundActor->SendClose();
}
// Decrease the number of active databases right after the database is
// closed.
MaybeDecreaseActiveDatabaseCount();
}
}
@ -690,10 +695,7 @@ IDBDatabase::Transaction(JSContext* aCx,
RefPtr<IDBTransaction> transaction =
IDBTransaction::Create(aCx, this, sortedStoreNames, mode);
if (NS_WARN_IF(!transaction)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
MOZ_ASSERT(transaction);
BackgroundTransactionChild* actor =
new BackgroundTransactionChild(transaction);
@ -967,19 +969,30 @@ IDBDatabase::NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor)
}
void
IDBDatabase::DelayedMaybeExpireFileActors()
IDBDatabase::NoteActiveTransaction()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFactory);
// Increase the number of active transactions.
mFactory->UpdateActiveTransactionCount(1);
}
void
IDBDatabase::NoteInactiveTransaction()
{
AssertIsOnOwningThread();
if (!mBackgroundActor || !mFileActors.Count()) {
MOZ_ASSERT(mFactory);
mFactory->UpdateActiveTransactionCount(-1);
return;
}
RefPtr<Runnable> runnable =
NewRunnableMethod<bool>("IDBDatabase::ExpireFileActors",
this,
&IDBDatabase::ExpireFileActors,
/* aExpireAll */ false);
NewRunnableMethod("IDBDatabase::NoteInactiveTransactionDelayed",
this,
&IDBDatabase::NoteInactiveTransactionDelayed);
MOZ_ASSERT(runnable);
if (!NS_IsMainThread()) {
@ -1131,6 +1144,15 @@ IDBDatabase::Invalidate()
}
}
void
IDBDatabase::NoteInactiveTransactionDelayed()
{
ExpireFileActors(/* aExpireAll */ false);
MOZ_ASSERT(mFactory);
mFactory->UpdateActiveTransactionCount(-1);
}
void
IDBDatabase::LogWarning(const char* aMessageName,
const nsAString& aFilename,
@ -1352,5 +1374,29 @@ IDBDatabase::RenameIndex(int64_t aObjectStoreId,
return NS_OK;
}
void
IDBDatabase::IncreaseActiveDatabaseCount()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFactory);
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
mFactory->UpdateActiveDatabaseCount(1);
mIncreasedActiveDatabaseCount = true;
}
void
IDBDatabase::MaybeDecreaseActiveDatabaseCount()
{
AssertIsOnOwningThread();
if (mIncreasedActiveDatabaseCount) {
// Decrease the number of active databases.
MOZ_ASSERT(mFactory);
mFactory->UpdateActiveDatabaseCount(-1);
mIncreasedActiveDatabaseCount = false;
}
}
} // namespace dom
} // namespace mozilla

View File

@ -86,6 +86,7 @@ class IDBDatabase final
bool mClosed;
bool mInvalidated;
bool mQuotaExceeded;
bool mIncreasedActiveDatabaseCount;
public:
static already_AddRefed<IDBDatabase>
@ -192,7 +193,10 @@ public:
NoteFinishedFileActor(indexedDB::PBackgroundIDBDatabaseFileChild* aFileActor);
void
DelayedMaybeExpireFileActors();
NoteActiveTransaction();
void
NoteInactiveTransaction();
// XXX This doesn't really belong here... It's only needed for IDBMutableFile
// serialization and should be removed or fixed someday.
@ -267,6 +271,10 @@ public:
{
AssertIsOnOwningThread();
// Decrease the number of active databases if it was not done in
// CloseInternal().
MaybeDecreaseActiveDatabaseCount();
mBackgroundActor = nullptr;
}
@ -321,6 +329,9 @@ private:
void
InvalidateMutableFiles();
void
NoteInactiveTransactionDelayed();
void
LogWarning(const char* aMessageName,
const nsAString& aFilename,
@ -334,6 +345,12 @@ private:
// Only accessed by IDBIndex.
nsresult
RenameIndex(int64_t aObjectStoreId, int64_t aIndexId, const nsAString& aName);
void
IncreaseActiveDatabaseCount();
void
MaybeDecreaseActiveDatabaseCount();
};
} // namespace dom

View File

@ -162,13 +162,17 @@ IDBFactory::CreateForWindow(nsPIDOMWindowInner* aWindow,
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
RefPtr<nsGlobalWindow> globalWindow = nsGlobalWindow::Cast(aWindow);
MOZ_ASSERT(globalWindow);
nsCOMPtr<nsPIDOMWindowOuter> topOutterWindow = globalWindow->GetScriptableTop();
MOZ_ASSERT(topOutterWindow);
RefPtr<IDBFactory> factory = new IDBFactory();
factory->mPrincipalInfo = Move(principalInfo);
factory->mWindow = aWindow;
factory->mTopWindow = topOutterWindow->GetCurrentInnerWindow();
factory->mTabChild = TabChild::GetFrom(aWindow);
factory->mEventTarget =
nsGlobalWindow::Cast(aWindow)->EventTargetFor(TaskCategory::Other);
factory->mEventTarget = globalWindow->EventTargetFor(TaskCategory::Other);
factory->mInnerWindowID = aWindow->WindowID();
factory->mPrivateBrowsingMode =
loadContext && loadContext->UsePrivateBrowsing();
@ -417,6 +421,24 @@ IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal,
return true;
}
void
IDBFactory::UpdateActiveTransactionCount(int32_t aDelta)
{
AssertIsOnOwningThread();
if (mTopWindow) {
mTopWindow->UpdateActiveIndexedDBTransactionCount(aDelta);
}
}
void
IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta)
{
AssertIsOnOwningThread();
if (mTopWindow) {
mTopWindow->UpdateActiveIndexedDBDatabaseCount(aDelta);
}
}
bool
IDBFactory::IsChrome() const
{
@ -892,12 +914,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mOwningObject = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)

View File

@ -60,9 +60,10 @@ class IDBFactory final
nsAutoPtr<PrincipalInfo> mPrincipalInfo;
// If this factory lives on a window then mWindow must be non-null. Otherwise
// mOwningObject must be non-null.
// If this factory lives on a window then m(Top)Window must be non-null.
// Otherwise mOwningObject must be non-null.
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsPIDOMWindowInner> mTopWindow;
JS::Heap<JSObject*> mOwningObject;
// This will only be set if the factory belongs to a window in a child
@ -129,6 +130,20 @@ public:
mBackgroundActor = nullptr;
}
// Increase/Decrease the number of active transactions for the decision
// making of preemption and throttling.
// Note: If the state of its actor is not committed or aborted, it could block
// IDB operations in other window.
void
UpdateActiveTransactionCount(int32_t aDelta);
// Increase/Decrease the number of active databases and IDBOpenRequests for
// the decision making of preemption and throttling.
// Note: A non-closed database or a pending IDBOpenRequest could block
// IDB operations in other window.
void
UpdateActiveDatabaseCount(int32_t aDelta);
void
IncrementParentLoggingRequestSerialNumber();

View File

@ -210,29 +210,6 @@ IDBRequest::Reset()
mError = nullptr;
}
void
IDBRequest::DispatchNonTransactionError(nsresult aErrorCode)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aErrorCode));
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
SetError(aErrorCode);
// Make an error event and fire it at the target.
nsCOMPtr<nsIDOMEvent> event =
CreateGenericEvent(this,
nsDependentString(kErrorEventType),
eDoesBubble,
eCancelable);
MOZ_ASSERT(event);
bool ignored;
if (NS_FAILED(DispatchEvent(event, &ignored))) {
NS_WARNING("Failed to dispatch event!");
}
}
void
IDBRequest::SetError(nsresult aRv)
{
@ -491,6 +468,7 @@ IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory,
: IDBRequest(aOwner)
, mFactory(aFactory)
, mFileHandleDisabled(aFileHandleDisabled)
, mIncreasedActiveDatabaseCount(false)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFactory);
@ -501,6 +479,7 @@ IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory,
IDBOpenDBRequest::~IDBOpenDBRequest()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
}
// static
@ -523,6 +502,8 @@ IDBOpenDBRequest::CreateForWindow(JSContext* aCx,
request->SetScriptOwner(aScriptOwner);
request->IncreaseActiveDatabaseCount();
return request.forget();
}
@ -559,6 +540,8 @@ IDBOpenDBRequest::CreateForJS(JSContext* aCx,
request->mWorkerHolder = Move(workerHolder);
}
request->IncreaseActiveDatabaseCount();
return request.forget();
}
@ -572,17 +555,74 @@ IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction)
mTransaction = aTransaction;
}
void
IDBOpenDBRequest::DispatchNonTransactionError(nsresult aErrorCode)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aErrorCode));
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
// The actor failed to initiate, decrease the number of active IDBOpenRequests
// here since NoteComplete won't be called.
MaybeDecreaseActiveDatabaseCount();
SetError(aErrorCode);
// Make an error event and fire it at the target.
nsCOMPtr<nsIDOMEvent> event =
CreateGenericEvent(this,
nsDependentString(kErrorEventType),
eDoesBubble,
eCancelable);
MOZ_ASSERT(event);
bool ignored;
if (NS_FAILED(DispatchEvent(event, &ignored))) {
NS_WARNING("Failed to dispatch event!");
}
}
void
IDBOpenDBRequest::NoteComplete()
{
AssertIsOnOwningThread();
MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
// Normally, we decrease the number of active IDBOpenRequests here.
MaybeDecreaseActiveDatabaseCount();
// If we have a WorkerHolder installed on the worker then nulling this out
// will uninstall it from the worker.
mWorkerHolder = nullptr;
}
void
IDBOpenDBRequest::IncreaseActiveDatabaseCount()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
// Increase the number of active IDBOpenRequests.
// Note: We count here instead of the actor's ctor because the preemption
// could happen at next JS interrupt but its BackgroundFactoryRequestChild
// could be created asynchronously from IDBFactory::BackgroundCreateCallback
// ::ActorCreated() if its PBackgroundChild is not created yet on this thread.
mFactory->UpdateActiveDatabaseCount(1);
mIncreasedActiveDatabaseCount = true;
}
void
IDBOpenDBRequest::MaybeDecreaseActiveDatabaseCount()
{
AssertIsOnOwningThread();
if (mIncreasedActiveDatabaseCount) {
// Decrease the number of active IDBOpenRequests.
mFactory->UpdateActiveDatabaseCount(-1);
mIncreasedActiveDatabaseCount = false;
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest,

View File

@ -93,9 +93,6 @@ public:
void
Reset();
void
DispatchNonTransactionError(nsresult aErrorCode);
void
SetResultCallback(ResultCallback* aCallback);
@ -227,6 +224,7 @@ class IDBOpenDBRequest final
nsAutoPtr<WorkerHolder> mWorkerHolder;
const bool mFileHandleDisabled;
bool mIncreasedActiveDatabaseCount;
public:
static already_AddRefed<IDBOpenDBRequest>
@ -249,6 +247,9 @@ public:
void
SetTransaction(IDBTransaction* aTransaction);
void
DispatchNonTransactionError(nsresult aErrorCode);
void
NoteComplete();
@ -278,6 +279,12 @@ private:
bool aFileHandleDisabled);
~IDBOpenDBRequest();
void
IncreaseActiveDatabaseCount();
void
MaybeDecreaseActiveDatabaseCount();
};
} // namespace dom

View File

@ -88,6 +88,7 @@ IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
, mCreating(false)
, mRegistered(false)
, mAbortedByScript(false)
, mNotedActiveTransaction(false)
#ifdef DEBUG
, mSentCommitOrAbort(false)
, mFiredCompleteOrAbort(false)
@ -134,6 +135,7 @@ IDBTransaction::~IDBTransaction()
AssertIsOnOwningThread();
MOZ_ASSERT(!mPendingRequestCount);
MOZ_ASSERT(!mCreating);
MOZ_ASSERT(!mNotedActiveTransaction);
MOZ_ASSERT(mSentCommitOrAbort);
MOZ_ASSERT_IF(mMode == VERSION_CHANGE &&
mBackgroundActor.mVersionChangeBackgroundActor,
@ -194,6 +196,8 @@ IDBTransaction::CreateVersionChange(
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
nsContentUtils::RunInMetastableState(runnable.forget());
transaction->NoteActiveTransaction();
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
transaction->mNextObjectStoreId = aNextObjectStoreId;
transaction->mNextIndexId = aNextIndexId;
@ -284,6 +288,8 @@ IDBTransaction::SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackg
MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
MOZ_ASSERT(mMode != VERSION_CHANGE);
NoteActiveTransaction();
mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
}
@ -474,6 +480,27 @@ IDBTransaction::SendAbort(nsresult aResultCode)
#endif
}
void
IDBTransaction::NoteActiveTransaction()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mNotedActiveTransaction);
mDatabase->NoteActiveTransaction();
mNotedActiveTransaction = true;
}
void
IDBTransaction::MaybeNoteInactiveTransaction()
{
AssertIsOnOwningThread();
if (mNotedActiveTransaction) {
mDatabase->NoteInactiveTransaction();
mNotedActiveTransaction = false;
}
}
bool
IDBTransaction::IsOpen() const
{
@ -834,7 +861,15 @@ IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
NS_WARNING("DispatchEvent failed!");
}
mDatabase->DelayedMaybeExpireFileActors();
// Normally, we note inactive transaction here instead of
// IDBTransaction::ClearBackgroundActor() because here is the earliest place
// to know that it becomes non-blocking to allow the scheduler to start the
// preemption as soon as it can.
// Note: If the IDBTransaction object is held by the script,
// ClearBackgroundActor() will be done in ~IDBTransaction() until garbage
// collected after its window is closed which prevents us to preempt its
// window immediately after committed.
MaybeNoteInactiveTransaction();
}
int64_t

View File

@ -109,6 +109,7 @@ private:
bool mCreating;
bool mRegistered;
bool mAbortedByScript;
bool mNotedActiveTransaction;
#ifdef DEBUG
bool mSentCommitOrAbort;
@ -152,6 +153,10 @@ public:
} else {
mBackgroundActor.mNormalBackgroundActor = nullptr;
}
// Note inactive transaction here if we didn't receive the Complete message
// from the parent.
MaybeNoteInactiveTransaction();
}
indexedDB::BackgroundRequestChild*
@ -331,6 +336,12 @@ private:
void
SendAbort(nsresult aResultCode);
void
NoteActiveTransaction();
void
MaybeNoteInactiveTransaction();
void
OnNewRequest();