Bug 1143885 - Fix transaction handling when requests are killed prematurely, r=khuey.

This commit is contained in:
Ben Turner 2015-03-18 14:20:59 -07:00
parent f04229dfdd
commit d405a7eb4f
6 changed files with 151 additions and 143 deletions

View File

@ -1828,18 +1828,14 @@ BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
mTransaction->AssertIsOnOwningThread();
MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
mTransaction->OnNewRequest();
}
BackgroundRequestChild::~BackgroundRequestChild()
{
AssertIsOnOwningThread();
MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction);
MOZ_ASSERT(!mTransaction);
MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
MaybeFinishTransactionEarly();
}
void
@ -1853,19 +1849,6 @@ BackgroundRequestChild::HoldFileInfosUntilComplete(
}
void
BackgroundRequestChild::MaybeFinishTransactionEarly()
{
AssertIsOnOwningThread();
if (mTransaction) {
mTransaction->AssertIsOnOwningThread();
mTransaction->OnRequestFinished();
mTransaction = nullptr;
}
}
bool
BackgroundRequestChild::HandleResponse(nsresult aResponse)
{
AssertIsOnOwningThread();
@ -1874,10 +1857,9 @@ BackgroundRequestChild::HandleResponse(nsresult aResponse)
MOZ_ASSERT(mTransaction);
DispatchErrorEvent(mRequest, aResponse, mTransaction);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(const Key& aResponse)
{
AssertIsOnOwningThread();
@ -1885,10 +1867,9 @@ BackgroundRequestChild::HandleResponse(const Key& aResponse)
ResultHelper helper(mRequest, mTransaction, &aResponse);
DispatchSuccessEvent(&helper);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
{
AssertIsOnOwningThread();
@ -1896,10 +1877,9 @@ BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
ResultHelper helper(mRequest, mTransaction, &aResponse);
DispatchSuccessEvent(&helper);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(
const SerializedStructuredCloneReadInfo& aResponse)
{
@ -1919,10 +1899,9 @@ BackgroundRequestChild::HandleResponse(
ResultHelper helper(mRequest, mTransaction, &cloneReadInfo);
DispatchSuccessEvent(&helper);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(
const nsTArray<SerializedStructuredCloneReadInfo>& aResponse)
{
@ -1957,10 +1936,9 @@ BackgroundRequestChild::HandleResponse(
ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
DispatchSuccessEvent(&helper);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
{
AssertIsOnOwningThread();
@ -1968,10 +1946,9 @@ BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
ResultHelper helper(mRequest, mTransaction, &aResponse);
DispatchSuccessEvent(&helper);
return true;
}
bool
void
BackgroundRequestChild::HandleResponse(uint64_t aResponse)
{
AssertIsOnOwningThread();
@ -1981,7 +1958,6 @@ BackgroundRequestChild::HandleResponse(uint64_t aResponse)
ResultHelper helper(mRequest, mTransaction, &response);
DispatchSuccessEvent(&helper);
return true;
}
void
@ -1991,9 +1967,17 @@ BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy)
MaybeCollectGarbageOnIPCMessage();
MaybeFinishTransactionEarly();
NoteActorDestroyed();
if (mTransaction) {
mTransaction->AssertIsOnOwningThread();
mTransaction->OnRequestFinished(/* aActorDestroyedNormally */
aWhy == Deletion);
#ifdef DEBUG
mTransaction = nullptr;
#endif
}
}
bool
@ -2005,62 +1989,80 @@ BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
MaybeCollectGarbageOnIPCMessage();
// Always fire an "error" event with ABORT_ERR if the transaction was aborted,
// even if the request succeeded or failed with another error.
if (mTransaction->IsAborted()) {
return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
// Always fire an "error" event with ABORT_ERR if the transaction was
// aborted, even if the request succeeded or failed with another error.
HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
} else {
switch (aResponse.type()) {
case RequestResponse::Tnsresult:
HandleResponse(aResponse.get_nsresult());
break;
case RequestResponse::TObjectStoreAddResponse:
HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
break;
case RequestResponse::TObjectStorePutResponse:
HandleResponse(aResponse.get_ObjectStorePutResponse().key());
break;
case RequestResponse::TObjectStoreGetResponse:
HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
break;
case RequestResponse::TObjectStoreGetAllResponse:
HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos());
break;
case RequestResponse::TObjectStoreGetAllKeysResponse:
HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
break;
case RequestResponse::TObjectStoreDeleteResponse:
HandleResponse(JS::UndefinedHandleValue);
break;
case RequestResponse::TObjectStoreClearResponse:
HandleResponse(JS::UndefinedHandleValue);
break;
case RequestResponse::TObjectStoreCountResponse:
HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
break;
case RequestResponse::TIndexGetResponse:
HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
break;
case RequestResponse::TIndexGetKeyResponse:
HandleResponse(aResponse.get_IndexGetKeyResponse().key());
break;
case RequestResponse::TIndexGetAllResponse:
HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
break;
case RequestResponse::TIndexGetAllKeysResponse:
HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
break;
case RequestResponse::TIndexCountResponse:
HandleResponse(aResponse.get_IndexCountResponse().count());
break;
default:
MOZ_CRASH("Unknown response type!");
}
}
switch (aResponse.type()) {
case RequestResponse::Tnsresult:
return HandleResponse(aResponse.get_nsresult());
mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
case RequestResponse::TObjectStoreAddResponse:
return HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
// Null this out so that we don't try to call OnRequestFinished() again in
// ActorDestroy.
mTransaction = nullptr;
case RequestResponse::TObjectStorePutResponse:
return HandleResponse(aResponse.get_ObjectStorePutResponse().key());
case RequestResponse::TObjectStoreGetResponse:
return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
case RequestResponse::TObjectStoreGetAllResponse:
return HandleResponse(aResponse.get_ObjectStoreGetAllResponse()
.cloneInfos());
case RequestResponse::TObjectStoreGetAllKeysResponse:
return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse()
.keys());
case RequestResponse::TObjectStoreDeleteResponse:
return HandleResponse(JS::UndefinedHandleValue);
case RequestResponse::TObjectStoreClearResponse:
return HandleResponse(JS::UndefinedHandleValue);
case RequestResponse::TObjectStoreCountResponse:
return HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
case RequestResponse::TIndexGetResponse:
return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
case RequestResponse::TIndexGetKeyResponse:
return HandleResponse(aResponse.get_IndexGetKeyResponse().key());
case RequestResponse::TIndexGetAllResponse:
return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
case RequestResponse::TIndexGetAllKeysResponse:
return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
case RequestResponse::TIndexCountResponse:
return HandleResponse(aResponse.get_IndexCountResponse().count());
default:
MOZ_CRASH("Unknown response type!");
}
MOZ_CRASH("Should never get here!");
return true;
}
/*******************************************************************************
@ -2370,7 +2372,8 @@ BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy)
MaybeCollectGarbageOnIPCMessage();
if (mStrongRequest && !mStrongCursor && mTransaction) {
mTransaction->OnRequestFinished();
mTransaction->OnRequestFinished(/* aActorDestroyedNormally */
aWhy == Deletion);
}
if (mCursor) {
@ -2435,7 +2438,7 @@ BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse)
MOZ_CRASH("Should never get here!");
}
mTransaction->OnRequestFinished();
mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
return true;
}

View File

@ -561,43 +561,43 @@ class BackgroundRequestChild MOZ_FINAL
{
friend class BackgroundTransactionChild;
friend class BackgroundVersionChangeTransactionChild;
friend class IDBTransaction;
nsRefPtr<IDBTransaction> mTransaction;
nsTArray<nsRefPtr<FileInfo>> mFileInfos;
public:
explicit BackgroundRequestChild(IDBRequest* aRequest);
void
HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
private:
// Only created by IDBTransaction.
explicit
BackgroundRequestChild(IDBRequest* aRequest);
// Only destroyed by BackgroundTransactionChild or
// BackgroundVersionChangeTransactionChild.
~BackgroundRequestChild();
void
MaybeFinishTransactionEarly();
bool
HandleResponse(nsresult aResponse);
bool
void
HandleResponse(const Key& aResponse);
bool
void
HandleResponse(const nsTArray<Key>& aResponse);
bool
void
HandleResponse(const SerializedStructuredCloneReadInfo& aResponse);
bool
void
HandleResponse(const nsTArray<SerializedStructuredCloneReadInfo>& aResponse);
bool
void
HandleResponse(JS::Handle<JS::Value> aResponse);
bool
void
HandleResponse(uint64_t aResponse);
// IPDL methods are only called by IPDL.

View File

@ -292,9 +292,7 @@ IDBIndex::GetInternal(bool aKeyOnly,
IDB_LOG_STRINGIFY(keyRange));
}
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
transaction->StartRequest(actor, params);
transaction->StartRequest(request, params);
return request.forget();
}
@ -375,9 +373,7 @@ IDBIndex::GetAllInternal(bool aKeysOnly,
IDB_LOG_STRINGIFY(aLimit));
}
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
transaction->StartRequest(actor, params);
transaction->StartRequest(request, params);
return request.forget();
}
@ -527,9 +523,7 @@ IDBIndex::Count(JSContext* aCx,
IDB_LOG_STRINGIFY(this),
IDB_LOG_STRINGIFY(keyRange));
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
transaction->StartRequest(actor, params);
transaction->StartRequest(request, params);
return request.forget();
}

View File

@ -1279,9 +1279,8 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
}
}
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
BackgroundRequestChild* actor = mTransaction->StartRequest(request, params);
MOZ_ASSERT(actor);
if (!fileInfosToKeepAlive.IsEmpty()) {
nsTArray<nsRefPtr<FileInfo>> fileInfos;
@ -1365,9 +1364,7 @@ IDBObjectStore::GetAllInternal(bool aKeysOnly,
IDB_LOG_STRINGIFY(aLimit));
}
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
mTransaction->StartRequest(request, params);
return request.forget();
}
@ -1403,9 +1400,7 @@ IDBObjectStore::Clear(ErrorResult& aRv)
IDB_LOG_STRINGIFY(mTransaction),
IDB_LOG_STRINGIFY(this));
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
mTransaction->StartRequest(request, params);
return request.forget();
}
@ -1600,9 +1595,7 @@ IDBObjectStore::Get(JSContext* aCx,
IDB_LOG_STRINGIFY(this),
IDB_LOG_STRINGIFY(keyRange));
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
mTransaction->StartRequest(request, params);
return request.forget();
}
@ -1657,9 +1650,7 @@ IDBObjectStore::DeleteInternal(JSContext* aCx,
IDB_LOG_STRINGIFY(keyRange));
}
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
mTransaction->StartRequest(request, params);
return request.forget();
}
@ -1899,9 +1890,7 @@ IDBObjectStore::Count(JSContext* aCx,
IDB_LOG_STRINGIFY(this),
IDB_LOG_STRINGIFY(keyRange));
BackgroundRequestChild* actor = new BackgroundRequestChild(request);
mTransaction->StartRequest(actor, params);
mTransaction->StartRequest(request, params);
return request.forget();
}

View File

@ -325,25 +325,31 @@ IDBTransaction::SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor)
mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
}
void
IDBTransaction::StartRequest(BackgroundRequestChild* aBackgroundActor,
const RequestParams& aParams)
BackgroundRequestChild*
IDBTransaction::StartRequest(IDBRequest* aRequest, const RequestParams& aParams)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aBackgroundActor);
MOZ_ASSERT(aRequest);
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
BackgroundRequestChild* actor = new BackgroundRequestChild(aRequest);
if (mMode == VERSION_CHANGE) {
MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
mBackgroundActor.mVersionChangeBackgroundActor->
SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams);
SendPBackgroundIDBRequestConstructor(actor, aParams);
} else {
MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
mBackgroundActor.mNormalBackgroundActor->
SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams);
SendPBackgroundIDBRequestConstructor(actor, aParams);
}
// Balanced in BackgroundRequestChild::Recv__delete__().
OnNewRequest();
return actor;
}
void
@ -402,7 +408,7 @@ IDBTransaction::OnNewRequest()
}
void
IDBTransaction::OnRequestFinished()
IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mPendingRequestCount);
@ -412,10 +418,24 @@ IDBTransaction::OnRequestFinished()
if (!mPendingRequestCount && !mDatabase->IsInvalidated()) {
mReadyState = COMMITTING;
if (NS_SUCCEEDED(mAbortCode)) {
SendCommit();
if (aActorDestroyedNormally) {
if (NS_SUCCEEDED(mAbortCode)) {
SendCommit();
} else {
SendAbort(mAbortCode);
}
} else {
SendAbort(mAbortCode);
// Don't try to send any more messages to the parent if the request actor
// was killed.
#ifdef DEBUG
MOZ_ASSERT(!mSentCommitOrAbort);
mSentCommitOrAbort = true;
#endif
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld]: "
"Request actor was killed, transaction will be aborted",
"IndexedDB %s: C T[%lld]: IDBTransaction abort",
IDB_LOG_ID_STRING(),
LoggingSerialNumber());
}
}
}

View File

@ -49,6 +49,9 @@ class IDBTransaction MOZ_FINAL
: public IDBWrapperCache
, public nsIRunnable
{
friend class BackgroundCursorChild;
friend class BackgroundRequestChild;
class WorkerFeature;
friend class WorkerFeature;
@ -150,9 +153,8 @@ public:
}
}
void
StartRequest(BackgroundRequestChild* aBackgroundActor,
const RequestParams& aParams);
BackgroundRequestChild*
StartRequest(IDBRequest* aRequest, const RequestParams& aParams);
void
OpenCursor(BackgroundCursorChild* aBackgroundActor,
@ -161,12 +163,6 @@ public:
void
RefreshSpec(bool aMayDelete);
void
OnNewRequest();
void
OnRequestFinished();
bool
IsOpen() const;
@ -314,6 +310,12 @@ private:
void
SendAbort(nsresult aResultCode);
void
OnNewRequest();
void
OnRequestFinished(bool aActorDestroyedNormally);
};
} // namespace indexedDB