mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1609957 - Fix race condition in determining the last request before commit. r=dom-workers-and-storage-reviewers,janv
Differential Revision: https://phabricator.services.mozilla.com/D60642 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
81fb58821d
commit
fff839290a
@ -6306,7 +6306,9 @@ class TransactionBase {
|
|||||||
FlippedOnce<false> mCommitOrAbortReceived;
|
FlippedOnce<false> mCommitOrAbortReceived;
|
||||||
FlippedOnce<false> mCommittedOrAborted;
|
FlippedOnce<false> mCommittedOrAborted;
|
||||||
FlippedOnce<false> mForceAborted;
|
FlippedOnce<false> mForceAborted;
|
||||||
bool mHasFailedRequest = false;
|
InitializedOnce<const Maybe<int64_t>, LazyInit::Allow>
|
||||||
|
mLastRequestBeforeCommit;
|
||||||
|
Maybe<int64_t> mLastFailedRequest;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void AssertIsOnConnectionThread() const {
|
void AssertIsOnConnectionThread() const {
|
||||||
@ -6397,7 +6399,7 @@ class TransactionBase {
|
|||||||
|
|
||||||
void NoteActiveRequest();
|
void NoteActiveRequest();
|
||||||
|
|
||||||
void NoteFinishedRequest(nsresult aResultCode);
|
void NoteFinishedRequest(int64_t aRequestId, nsresult aResultCode);
|
||||||
|
|
||||||
void Invalidate();
|
void Invalidate();
|
||||||
|
|
||||||
@ -6417,11 +6419,11 @@ class TransactionBase {
|
|||||||
void FakeActorDestroyed() { mActorDestroyed.EnsureFlipped(); }
|
void FakeActorDestroyed() { mActorDestroyed.EnsureFlipped(); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool RecvCommit();
|
bool RecvCommit(Maybe<int64_t> aLastRequest);
|
||||||
|
|
||||||
bool RecvAbort(nsresult aResultCode);
|
bool RecvAbort(nsresult aResultCode);
|
||||||
|
|
||||||
void MaybeCommitOrAbort(const nsresult aResultCode) {
|
void MaybeCommitOrAbort() {
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
// If we've already committed or aborted then there's nothing else to do.
|
// If we've already committed or aborted then there's nothing else to do.
|
||||||
@ -6442,13 +6444,6 @@ class TransactionBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note failed request, but ignore requests that failed before we were
|
|
||||||
// committing (cf. https://w3c.github.io/IndexedDB/#async-execute-request
|
|
||||||
// step 5.3 vs. 5.4).
|
|
||||||
if (NS_FAILED(aResultCode)) {
|
|
||||||
mHasFailedRequest = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommitOrAbort();
|
CommitOrAbort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6548,7 +6543,8 @@ class NormalTransaction final : public TransactionBase,
|
|||||||
|
|
||||||
mozilla::ipc::IPCResult RecvDeleteMe() override;
|
mozilla::ipc::IPCResult RecvDeleteMe() override;
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvCommit() override;
|
mozilla::ipc::IPCResult RecvCommit(
|
||||||
|
const Maybe<int64_t>& aLastRequest) override;
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
|
mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
|
||||||
|
|
||||||
@ -6608,7 +6604,8 @@ class VersionChangeTransaction final
|
|||||||
|
|
||||||
mozilla::ipc::IPCResult RecvDeleteMe() override;
|
mozilla::ipc::IPCResult RecvDeleteMe() override;
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvCommit() override;
|
mozilla::ipc::IPCResult RecvCommit(
|
||||||
|
const Maybe<int64_t>& aLastRequest) override;
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
|
mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
|
||||||
|
|
||||||
@ -14081,10 +14078,10 @@ void TransactionBase::Abort(nsresult aResultCode, bool aForce) {
|
|||||||
mForceAborted.EnsureFlipped();
|
mForceAborted.EnsureFlipped();
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeCommitOrAbort(mResultCode);
|
MaybeCommitOrAbort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransactionBase::RecvCommit() {
|
bool TransactionBase::RecvCommit(const Maybe<int64_t> aLastRequest) {
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (NS_WARN_IF(mCommitOrAbortReceived)) {
|
if (NS_WARN_IF(mCommitOrAbortReceived)) {
|
||||||
@ -14093,8 +14090,9 @@ bool TransactionBase::RecvCommit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mCommitOrAbortReceived.Flip();
|
mCommitOrAbortReceived.Flip();
|
||||||
|
mLastRequestBeforeCommit.init(aLastRequest);
|
||||||
|
|
||||||
MaybeCommitOrAbort(NS_OK);
|
MaybeCommitOrAbort();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14132,6 +14130,17 @@ void TransactionBase::CommitOrAbort() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In case of a failed request that was started after committing was
|
||||||
|
// initiated, abort (cf.
|
||||||
|
// https://w3c.github.io/IndexedDB/#async-execute-request step 5.3 vs. 5.4).
|
||||||
|
// Note this can only happen here when we are committing explicitly, otherwise
|
||||||
|
// the decision is made by the child.
|
||||||
|
if (NS_SUCCEEDED(mResultCode) && mLastFailedRequest &&
|
||||||
|
*mLastRequestBeforeCommit &&
|
||||||
|
*mLastFailedRequest >= **mLastRequestBeforeCommit) {
|
||||||
|
mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<CommitOp> commitOp = new CommitOp(this, ClampResultCode(mResultCode));
|
RefPtr<CommitOp> commitOp = new CommitOp(this, ClampResultCode(mResultCode));
|
||||||
|
|
||||||
gConnectionPool->Finish(TransactionId(), commitOp);
|
gConnectionPool->Finish(TransactionId(), commitOp);
|
||||||
@ -14641,13 +14650,18 @@ void TransactionBase::NoteActiveRequest() {
|
|||||||
mActiveRequestCount++;
|
mActiveRequestCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBase::NoteFinishedRequest(const nsresult aResultCode) {
|
void TransactionBase::NoteFinishedRequest(const int64_t aRequestId,
|
||||||
|
const nsresult aResultCode) {
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
MOZ_ASSERT(mActiveRequestCount);
|
MOZ_ASSERT(mActiveRequestCount);
|
||||||
|
|
||||||
mActiveRequestCount--;
|
mActiveRequestCount--;
|
||||||
|
|
||||||
MaybeCommitOrAbort(aResultCode);
|
if (NS_FAILED(aResultCode)) {
|
||||||
|
mLastFailedRequest = Some(aRequestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeCommitOrAbort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBase::Invalidate() {
|
void TransactionBase::Invalidate() {
|
||||||
@ -14923,7 +14937,7 @@ void NormalTransaction::ActorDestroy(ActorDestroyReason aWhy) {
|
|||||||
|
|
||||||
mForceAborted.EnsureFlipped();
|
mForceAborted.EnsureFlipped();
|
||||||
|
|
||||||
MaybeCommitOrAbort(mResultCode);
|
MaybeCommitOrAbort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14938,10 +14952,11 @@ mozilla::ipc::IPCResult NormalTransaction::RecvDeleteMe() {
|
|||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult NormalTransaction::RecvCommit() {
|
mozilla::ipc::IPCResult NormalTransaction::RecvCommit(
|
||||||
|
const Maybe<int64_t>& aLastRequest) {
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (!TransactionBase::RecvCommit()) {
|
if (!TransactionBase::RecvCommit(aLastRequest)) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
@ -15184,7 +15199,7 @@ void VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy) {
|
|||||||
|
|
||||||
mForceAborted.EnsureFlipped();
|
mForceAborted.EnsureFlipped();
|
||||||
|
|
||||||
MaybeCommitOrAbort(mResultCode);
|
MaybeCommitOrAbort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15199,10 +15214,11 @@ mozilla::ipc::IPCResult VersionChangeTransaction::RecvDeleteMe() {
|
|||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult VersionChangeTransaction::RecvCommit() {
|
mozilla::ipc::IPCResult VersionChangeTransaction::RecvCommit(
|
||||||
|
const Maybe<int64_t>& aLastRequest) {
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
if (!TransactionBase::RecvCommit()) {
|
if (!TransactionBase::RecvCommit(aLastRequest)) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
@ -22743,7 +22759,7 @@ void TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
|
|||||||
mWaitingForContinue = true;
|
mWaitingForContinue = true;
|
||||||
} else {
|
} else {
|
||||||
if (mLoggingSerialNumber) {
|
if (mLoggingSerialNumber) {
|
||||||
(*mTransaction)->NoteFinishedRequest(ResultCode());
|
(*mTransaction)->NoteFinishedRequest(mLoggingSerialNumber, ResultCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
Cleanup();
|
Cleanup();
|
||||||
@ -22926,10 +22942,6 @@ TransactionBase::CommitOp::Run() {
|
|||||||
MOZ_ASSERT(mTransaction);
|
MOZ_ASSERT(mTransaction);
|
||||||
mTransaction->AssertIsOnConnectionThread();
|
mTransaction->AssertIsOnConnectionThread();
|
||||||
|
|
||||||
if (NS_SUCCEEDED(mResultCode) && mTransaction->mHasFailedRequest) {
|
|
||||||
mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", DOM);
|
AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", DOM);
|
||||||
|
|
||||||
IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
|
IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
|
||||||
|
@ -386,7 +386,35 @@ void IDBTransaction::SendCommit(const bool aAutoCommit) {
|
|||||||
LoggingSerialNumber(), requestSerialNumber,
|
LoggingSerialNumber(), requestSerialNumber,
|
||||||
aAutoCommit ? "automatically" : "explicitly");
|
aAutoCommit ? "automatically" : "explicitly");
|
||||||
|
|
||||||
DoWithTransactionChild([](auto& actor) { actor.SendCommit(); });
|
const auto lastRequestSerialNumber =
|
||||||
|
[this, aAutoCommit,
|
||||||
|
requestSerialNumber]() -> Maybe<decltype(requestSerialNumber)> {
|
||||||
|
if (aAutoCommit) {
|
||||||
|
return Nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case of an explicit commit, we need to note the serial number of the
|
||||||
|
// last request to check if a request submitted before the commit request
|
||||||
|
// failed. If we are currently in an event handler for a request on this
|
||||||
|
// transaction, ignore this request. This is used to synchronize the
|
||||||
|
// transaction's committing state with the parent side, to abort the
|
||||||
|
// transaction in case of a request resulting in an error (see
|
||||||
|
// https://w3c.github.io/IndexedDB/#async-execute-request, step 5.3.). With
|
||||||
|
// automatic commit, this is not necessary, as the transaction's state will
|
||||||
|
// only be set to committing after the last request completed.
|
||||||
|
const bool dispatchingEventForThisTransaction =
|
||||||
|
BackgroundChildImpl::GetThreadLocalForCurrentThread()
|
||||||
|
->mIndexedDBThreadLocal->GetCurrentTransaction() == this;
|
||||||
|
|
||||||
|
return Some(requestSerialNumber
|
||||||
|
? (requestSerialNumber -
|
||||||
|
(dispatchingEventForThisTransaction ? 0 : 1))
|
||||||
|
: 0);
|
||||||
|
}();
|
||||||
|
|
||||||
|
DoWithTransactionChild([lastRequestSerialNumber](auto& actor) {
|
||||||
|
actor.SendCommit(lastRequestSerialNumber);
|
||||||
|
});
|
||||||
|
|
||||||
mSentCommitOrAbort.Flip();
|
mSentCommitOrAbort.Flip();
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,7 @@ namespace mozilla {
|
|||||||
namespace dom {
|
namespace dom {
|
||||||
namespace indexedDB {
|
namespace indexedDB {
|
||||||
|
|
||||||
protocol PBackgroundIDBTransaction
|
protocol PBackgroundIDBTransaction {
|
||||||
{
|
|
||||||
manager PBackgroundIDBDatabase;
|
manager PBackgroundIDBDatabase;
|
||||||
|
|
||||||
manages PBackgroundIDBCursor;
|
manages PBackgroundIDBCursor;
|
||||||
@ -24,7 +23,13 @@ protocol PBackgroundIDBTransaction
|
|||||||
parent:
|
parent:
|
||||||
async DeleteMe();
|
async DeleteMe();
|
||||||
|
|
||||||
async Commit();
|
// lastRequest is used with explicit commit to synchronize the
|
||||||
|
// transaction's committing state with the parent side, to abort the
|
||||||
|
// transaction in case of a request resulting in an error (see
|
||||||
|
// https://w3c.github.io/IndexedDB/#async-execute-request, step 5.3.). With
|
||||||
|
// automatic commit, this is not necessary, as the transaction's state will
|
||||||
|
// only be set to committing after the last request completed.
|
||||||
|
async Commit(int64_t? lastRequest);
|
||||||
async Abort(nsresult resultCode);
|
async Abort(nsresult resultCode);
|
||||||
|
|
||||||
async PBackgroundIDBCursor(OpenCursorParams params);
|
async PBackgroundIDBCursor(OpenCursorParams params);
|
||||||
@ -37,6 +42,6 @@ child:
|
|||||||
async Complete(nsresult result);
|
async Complete(nsresult result);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace indexedDB
|
} // namespace indexedDB
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -7,10 +7,10 @@ include protocol PBackgroundIDBDatabase;
|
|||||||
include protocol PBackgroundIDBDatabaseFile;
|
include protocol PBackgroundIDBDatabaseFile;
|
||||||
include protocol PBackgroundIDBRequest;
|
include protocol PBackgroundIDBRequest;
|
||||||
include protocol PBackgroundMutableFile;
|
include protocol PBackgroundMutableFile;
|
||||||
include protocol PChildToParentStream; // FIXME: bug 792908
|
include protocol PChildToParentStream; // FIXME: bug 792908
|
||||||
include protocol PFileDescriptorSet; // FIXME: bug 792908
|
include protocol PFileDescriptorSet; // FIXME: bug 792908
|
||||||
include protocol PIPCBlobInputStream; // FIXME: bug 792908
|
include protocol PIPCBlobInputStream; // FIXME: bug 792908
|
||||||
include protocol PParentToChildStream; // FIXME: bug 792908
|
include protocol PParentToChildStream; // FIXME: bug 792908
|
||||||
|
|
||||||
include PBackgroundIDBSharedTypes;
|
include PBackgroundIDBSharedTypes;
|
||||||
|
|
||||||
@ -18,8 +18,7 @@ namespace mozilla {
|
|||||||
namespace dom {
|
namespace dom {
|
||||||
namespace indexedDB {
|
namespace indexedDB {
|
||||||
|
|
||||||
protocol PBackgroundIDBVersionChangeTransaction
|
protocol PBackgroundIDBVersionChangeTransaction {
|
||||||
{
|
|
||||||
manager PBackgroundIDBDatabase;
|
manager PBackgroundIDBDatabase;
|
||||||
|
|
||||||
manages PBackgroundIDBCursor;
|
manages PBackgroundIDBCursor;
|
||||||
@ -28,21 +27,16 @@ protocol PBackgroundIDBVersionChangeTransaction
|
|||||||
parent:
|
parent:
|
||||||
async DeleteMe();
|
async DeleteMe();
|
||||||
|
|
||||||
async Commit();
|
async Commit(int64_t? lastRequest);
|
||||||
async Abort(nsresult resultCode);
|
async Abort(nsresult resultCode);
|
||||||
|
|
||||||
async CreateObjectStore(ObjectStoreMetadata metadata);
|
async CreateObjectStore(ObjectStoreMetadata metadata);
|
||||||
async DeleteObjectStore(int64_t objectStoreId);
|
async DeleteObjectStore(int64_t objectStoreId);
|
||||||
async RenameObjectStore(int64_t objectStoreId,
|
async RenameObjectStore(int64_t objectStoreId, nsString name);
|
||||||
nsString name);
|
|
||||||
|
|
||||||
async CreateIndex(int64_t objectStoreId,
|
async CreateIndex(int64_t objectStoreId, IndexMetadata metadata);
|
||||||
IndexMetadata metadata);
|
async DeleteIndex(int64_t objectStoreId, int64_t indexId);
|
||||||
async DeleteIndex(int64_t objectStoreId,
|
async RenameIndex(int64_t objectStoreId, int64_t indexId, nsString name);
|
||||||
int64_t indexId);
|
|
||||||
async RenameIndex(int64_t objectStoreId,
|
|
||||||
int64_t indexId,
|
|
||||||
nsString name);
|
|
||||||
|
|
||||||
async PBackgroundIDBCursor(OpenCursorParams params);
|
async PBackgroundIDBCursor(OpenCursorParams params);
|
||||||
|
|
||||||
@ -54,6 +48,6 @@ child:
|
|||||||
async Complete(nsresult result);
|
async Complete(nsresult result);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace indexedDB
|
} // namespace indexedDB
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
Loading…
Reference in New Issue
Block a user